├── examples ├── snapshot │ ├── Settings.txt │ ├── Mode.txt │ ├── Beam.txt │ ├── Material.txt │ ├── Domain.txt │ ├── ParamInput.txt │ ├── Output.txt │ └── Path.txt ├── solidification │ ├── Settings.txt │ ├── Mode.txt │ ├── Beam.txt │ ├── Material.txt │ ├── Domain.txt │ ├── ParamInput.txt │ ├── Path.txt │ └── Output.txt ├── snapshot_T_hist │ ├── Settings.txt │ ├── Mode.txt │ ├── Beam.txt │ ├── Material.txt │ ├── Domain.txt │ ├── ParamInput.txt │ ├── Output.txt │ └── Path.txt ├── solidification_mpstats │ ├── Settings.txt │ ├── Path.txt │ ├── Mode.txt │ ├── Beam.txt │ ├── Material.txt │ ├── Domain.txt │ ├── ParamInput.txt │ └── Output.txt ├── solidification_multibeam │ ├── Settings.txt │ ├── Mode.txt │ ├── Path.1.txt │ ├── Path.2.txt │ ├── Beam.1.txt │ ├── Beam.2.txt │ ├── Material.txt │ ├── Domain.txt │ ├── ParamInput.txt │ └── Output.txt ├── solidification_stork │ ├── Settings.txt │ ├── Mode.txt │ ├── Path.txt │ ├── Beam.txt │ ├── Material.txt │ ├── Domain.txt │ ├── ParamInput.txt │ └── Output.txt └── run_examples.sh ├── bin ├── CMakeLists.txt └── Main.cpp ├── src ├── ThesisConfig.h.cmakein ├── CMakeLists.txt ├── Out.h ├── Calc.h ├── Run.h ├── Out.cpp ├── Melt.h ├── Util.h ├── Init.h ├── MpiStructs.h ├── DataStructs.h ├── Util.cpp ├── Grid.h ├── Grid.cpp ├── Melt.cpp ├── Calc.cpp └── Init.cpp ├── CITATION.bib ├── makefile ├── CMakeLists.txt ├── BUILD.md ├── LICENSE ├── .github └── workflows │ └── CI.yml └── README.md /examples/snapshot/Settings.txt: -------------------------------------------------------------------------------- 1 | Compute 2 | { 3 | MaxThreads 4 4 | } -------------------------------------------------------------------------------- /examples/solidification/Settings.txt: -------------------------------------------------------------------------------- 1 | Compute 2 | { 3 | MaxThreads 8 4 | } -------------------------------------------------------------------------------- /examples/snapshot_T_hist/Settings.txt: -------------------------------------------------------------------------------- 1 | Compute 2 | { 3 | MaxThreads 4 4 | } -------------------------------------------------------------------------------- /examples/solidification_mpstats/Settings.txt: -------------------------------------------------------------------------------- 1 | Compute 2 | { 3 | MaxThreads 4 4 | } -------------------------------------------------------------------------------- /examples/solidification_multibeam/Settings.txt: -------------------------------------------------------------------------------- 1 | Compute 2 | { 3 | MaxThreads 4 4 | } -------------------------------------------------------------------------------- /examples/solidification_stork/Settings.txt: -------------------------------------------------------------------------------- 1 | Compute 2 | { 3 | MaxThreads 4 4 | } -------------------------------------------------------------------------------- /examples/snapshot/Mode.txt: -------------------------------------------------------------------------------- 1 | Snapshots 2 | { 3 | ScanFracs 25,50,75,100 4 | Tracking None 5 | } -------------------------------------------------------------------------------- /examples/snapshot_T_hist/Mode.txt: -------------------------------------------------------------------------------- 1 | Snapshots 2 | { 3 | ScanFracs 25,50,75,100 4 | Tracking None 5 | } -------------------------------------------------------------------------------- /examples/solidification/Mode.txt: -------------------------------------------------------------------------------- 1 | Solidification 2 | { 3 | Tracking Surface 4 | Timestep 1e-4 5 | } 6 | 7 | 8 | -------------------------------------------------------------------------------- /examples/solidification_mpstats/Path.txt: -------------------------------------------------------------------------------- 1 | Mode X(mm) Y(mm) Z(mm) Pmod Time(s) 2 | 1 0 0 0 0 0 3 | 0 5 1 0 1 1 4 | 5 | -------------------------------------------------------------------------------- /examples/solidification_stork/Mode.txt: -------------------------------------------------------------------------------- 1 | Solidification 2 | { 3 | Tracking Stork 4 | Timestep 1e-4 5 | } 6 | 7 | 8 | -------------------------------------------------------------------------------- /examples/solidification_stork/Path.txt: -------------------------------------------------------------------------------- 1 | Mode X(mm) Y(mm) Z(mm) Pmod Time(s) 2 | 1 0 0 0 0 0 3 | 0 5 1 0 1 1 4 | 5 | -------------------------------------------------------------------------------- /examples/solidification_mpstats/Mode.txt: -------------------------------------------------------------------------------- 1 | Solidification 2 | { 3 | Tracking Surface 4 | Timestep 1e-4 5 | } 6 | 7 | 8 | -------------------------------------------------------------------------------- /examples/solidification_multibeam/Mode.txt: -------------------------------------------------------------------------------- 1 | Solidification 2 | { 3 | Tracking Surface 4 | Timestep 1e-4 5 | } 6 | 7 | 8 | -------------------------------------------------------------------------------- /examples/solidification_multibeam/Path.1.txt: -------------------------------------------------------------------------------- 1 | Mode X(mm) Y(mm) Z(mm) Pmod Time(s) 2 | 1 0 0 0 0 1.00E-04 3 | 0 5 1 0 1 1 4 | 5 | -------------------------------------------------------------------------------- /examples/solidification_multibeam/Path.2.txt: -------------------------------------------------------------------------------- 1 | Mode X(mm) Y(mm) Z(mm) Pmod Time(s) 2 | 1 5 0 0 0 1.00E-04 3 | 0 0 1 0 1 1 4 | 5 | 6 | -------------------------------------------------------------------------------- /bin/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | add_executable(3DThesis Main.cpp) 2 | target_link_libraries(3DThesis LINK_PUBLIC Thesis) 3 | install(TARGETS 3DThesis DESTINATION ${CMAKE_INSTALL_BINDIR}) 4 | -------------------------------------------------------------------------------- /examples/snapshot/Beam.txt: -------------------------------------------------------------------------------- 1 | Shape 2 | { 3 | Width_X 10.0e-6 4 | Width_Y 10.0e-6 5 | Depth_Z 10.0e-6 6 | } 7 | Intensity 8 | { 9 | Power 1200 10 | Efficiency 1.0 11 | } -------------------------------------------------------------------------------- /examples/snapshot/Material.txt: -------------------------------------------------------------------------------- 1 | Constants 2 | { 3 | T_0 1273.0 4 | T_L 1610.0 5 | k 26.6 6 | c 600.0 7 | p 7451.0 8 | } 9 | CET 10 | { 11 | N0 3e13 12 | n 3 13 | a 1.25e6 14 | } -------------------------------------------------------------------------------- /examples/snapshot_T_hist/Beam.txt: -------------------------------------------------------------------------------- 1 | Shape 2 | { 3 | Width_X 10.0e-6 4 | Width_Y 10.0e-6 5 | Depth_Z 10.0e-6 6 | } 7 | Intensity 8 | { 9 | Power 1200 10 | Efficiency 1.0 11 | } -------------------------------------------------------------------------------- /examples/solidification/Beam.txt: -------------------------------------------------------------------------------- 1 | Shape 2 | { 3 | Width_X 10.0e-6 4 | Width_Y 10.0e-6 5 | Depth_Z 10.0e-6 6 | } 7 | Intensity 8 | { 9 | Power 1200 10 | Efficiency 1.0 11 | } -------------------------------------------------------------------------------- /src/ThesisConfig.h.cmakein: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #define Thesis_VERSION "@PROJECT_VERSION@" 4 | 5 | #define Thesis_COMMIT_HASH "@Thesis_COMMIT_HASH@" 6 | 7 | #cmakedefine Thesis_ENABLE_MPI -------------------------------------------------------------------------------- /examples/solidification/Material.txt: -------------------------------------------------------------------------------- 1 | Constants 2 | { 3 | T_0 1273.0 4 | T_L 1610.0 5 | k 26.6 6 | c 600.0 7 | p 7451.0 8 | } 9 | CET 10 | { 11 | N0 3e13 12 | n 3 13 | a 1.25e6 14 | } -------------------------------------------------------------------------------- /examples/solidification_mpstats/Beam.txt: -------------------------------------------------------------------------------- 1 | Shape 2 | { 3 | Width_X 10.0e-6 4 | Width_Y 10.0e-6 5 | Depth_Z 10.0e-6 6 | } 7 | Intensity 8 | { 9 | Power 600 10 | Efficiency 1.0 11 | } -------------------------------------------------------------------------------- /examples/solidification_stork/Beam.txt: -------------------------------------------------------------------------------- 1 | Shape 2 | { 3 | Width_X 10.0e-6 4 | Width_Y 10.0e-6 5 | Depth_Z 10.0e-6 6 | } 7 | Intensity 8 | { 9 | Power 600 10 | Efficiency 1.0 11 | } -------------------------------------------------------------------------------- /examples/snapshot_T_hist/Material.txt: -------------------------------------------------------------------------------- 1 | Constants 2 | { 3 | T_0 1273.0 4 | T_L 1610.0 5 | k 26.6 6 | c 600.0 7 | p 7451.0 8 | } 9 | CET 10 | { 11 | N0 3e13 12 | n 3 13 | a 1.25e6 14 | } -------------------------------------------------------------------------------- /examples/solidification_multibeam/Beam.1.txt: -------------------------------------------------------------------------------- 1 | Shape 2 | { 3 | Width_X 10.0e-6 4 | Width_Y 10.0e-6 5 | Depth_Z 10.0e-6 6 | } 7 | Intensity 8 | { 9 | Power 600 10 | Efficiency 1.0 11 | } -------------------------------------------------------------------------------- /examples/solidification_multibeam/Beam.2.txt: -------------------------------------------------------------------------------- 1 | Shape 2 | { 3 | Width_X 10.0e-6 4 | Width_Y 10.0e-6 5 | Depth_Z 10.0e-6 6 | } 7 | Intensity 8 | { 9 | Power 600 10 | Efficiency 1.0 11 | } -------------------------------------------------------------------------------- /examples/solidification_mpstats/Material.txt: -------------------------------------------------------------------------------- 1 | Constants 2 | { 3 | T_0 1273.0 4 | T_L 1610.0 5 | k 26.6 6 | c 600.0 7 | p 7451.0 8 | } 9 | CET 10 | { 11 | N0 3e13 12 | n 3 13 | a 1.25e6 14 | } -------------------------------------------------------------------------------- /examples/solidification_multibeam/Material.txt: -------------------------------------------------------------------------------- 1 | Constants 2 | { 3 | T_0 1273.0 4 | T_L 1610.0 5 | k 26.6 6 | c 600.0 7 | p 7451.0 8 | } 9 | CET 10 | { 11 | N0 3e13 12 | n 3 13 | a 1.25e6 14 | } -------------------------------------------------------------------------------- /examples/solidification_stork/Material.txt: -------------------------------------------------------------------------------- 1 | Constants 2 | { 3 | T_0 1273.0 4 | T_L 1610.0 5 | k 26.6 6 | c 600.0 7 | p 7451.0 8 | } 9 | CET 10 | { 11 | N0 3e13 12 | n 3 13 | a 1.25e6 14 | } -------------------------------------------------------------------------------- /examples/snapshot/Domain.txt: -------------------------------------------------------------------------------- 1 | X 2 | { 3 | Min -0.001 4 | Max 0.006 5 | Res 2.5e-5 6 | } 7 | Y 8 | { 9 | Min -0.001 10 | Max 0.002 11 | Res 2.5e-5 12 | } 13 | Z 14 | { 15 | Min -0.001 16 | Max 0.000 17 | Num 1 18 | } -------------------------------------------------------------------------------- /examples/snapshot_T_hist/Domain.txt: -------------------------------------------------------------------------------- 1 | X 2 | { 3 | Min -0.001 4 | Max 0.006 5 | Res 2.5e-5 6 | } 7 | Y 8 | { 9 | Min -0.001 10 | Max 0.002 11 | Res 2.5e-5 12 | } 13 | Z 14 | { 15 | Min -0.001 16 | Max 0.000 17 | Num 1 18 | } -------------------------------------------------------------------------------- /examples/solidification/Domain.txt: -------------------------------------------------------------------------------- 1 | X 2 | { 3 | Min -0.001 4 | Max 0.006 5 | Res 2.5e-5 6 | } 7 | Y 8 | { 9 | Min -0.001 10 | Max 0.002 11 | Res 2.5e-5 12 | } 13 | Z 14 | { 15 | Min -0.001 16 | Max 0.000 17 | Res 2.5e-5 18 | } -------------------------------------------------------------------------------- /examples/solidification_stork/Domain.txt: -------------------------------------------------------------------------------- 1 | X 2 | { 3 | Min -0.001 4 | Max 0.006 5 | Res 5.0e-5 6 | } 7 | Y 8 | { 9 | Min -0.001 10 | Max 0.002 11 | Res 5.0e-5 12 | } 13 | Z 14 | { 15 | Min -0.001 16 | Max 0.000 17 | Res 2.5e-5 18 | } -------------------------------------------------------------------------------- /examples/solidification_mpstats/Domain.txt: -------------------------------------------------------------------------------- 1 | X 2 | { 3 | Min -0.001 4 | Max 0.006 5 | Res 5.0e-5 6 | } 7 | Y 8 | { 9 | Min -0.001 10 | Max 0.002 11 | Res 5.0e-5 12 | } 13 | Z 14 | { 15 | Min -0.001 16 | Max 0.000 17 | Res 2.5e-5 18 | } -------------------------------------------------------------------------------- /examples/solidification_multibeam/Domain.txt: -------------------------------------------------------------------------------- 1 | X 2 | { 3 | Min -0.001 4 | Max 0.006 5 | Res 2.5e-5 6 | } 7 | Y 8 | { 9 | Min -0.001 10 | Max 0.002 11 | Res 2.5e-5 12 | } 13 | Z 14 | { 15 | Min -0.001 16 | Max 0.000 17 | Res 2.5e-5 18 | } -------------------------------------------------------------------------------- /examples/snapshot/ParamInput.txt: -------------------------------------------------------------------------------- 1 | Simulation 2 | { 3 | Name snapshot 4 | Mode Mode.txt 5 | Material Material.txt 6 | Beam Beam.txt 7 | Path Path.txt 8 | } 9 | Options 10 | { 11 | Domain Domain.txt 12 | Output Output.txt 13 | Settings Settings.txt 14 | } 15 | -------------------------------------------------------------------------------- /examples/solidification/ParamInput.txt: -------------------------------------------------------------------------------- 1 | Simulation 2 | { 3 | Name solidification 4 | Mode Mode.txt 5 | Material Material.txt 6 | Beam Beam.txt 7 | Path Path.txt 8 | } 9 | Options 10 | { 11 | Domain Domain.txt 12 | Output Output.txt 13 | Settings Settings.txt 14 | } 15 | -------------------------------------------------------------------------------- /examples/solidification_stork/ParamInput.txt: -------------------------------------------------------------------------------- 1 | Simulation 2 | { 3 | Name stork 4 | Mode Mode.txt 5 | Material Material.txt 6 | Beam Beam.txt 7 | Path Path.txt 8 | } 9 | Options 10 | { 11 | Domain Domain.txt 12 | Output Output.txt 13 | Settings Settings.txt 14 | } 15 | -------------------------------------------------------------------------------- /examples/snapshot_T_hist/ParamInput.txt: -------------------------------------------------------------------------------- 1 | Simulation 2 | { 3 | Name snapshot_T_hist 4 | Mode Mode.txt 5 | Material Material.txt 6 | Beam Beam.txt 7 | Path Path.txt 8 | } 9 | Options 10 | { 11 | Domain Domain.txt 12 | Output Output.txt 13 | Settings Settings.txt 14 | } 15 | -------------------------------------------------------------------------------- /examples/solidification_mpstats/ParamInput.txt: -------------------------------------------------------------------------------- 1 | Simulation 2 | { 3 | Name solidification_mpstats 4 | Mode Mode.txt 5 | Material Material.txt 6 | Beam Beam.txt 7 | Path Path.txt 8 | } 9 | Options 10 | { 11 | Domain Domain.txt 12 | Output Output.txt 13 | Settings Settings.txt 14 | } 15 | -------------------------------------------------------------------------------- /examples/solidification_multibeam/ParamInput.txt: -------------------------------------------------------------------------------- 1 | Simulation 2 | { 3 | Name solidification_multibeam 4 | Mode Mode.txt 5 | Material Material.txt 6 | Beam Beam.*.txt 7 | Path Path.*.txt 8 | } 9 | Options 10 | { 11 | Domain Domain.txt 12 | Output Output.txt 13 | Settings Settings.txt 14 | } 15 | -------------------------------------------------------------------------------- /examples/snapshot/Output.txt: -------------------------------------------------------------------------------- 1 | Grid 2 | { 3 | x 1 4 | y 1 5 | z 1 6 | } 7 | Temperature 8 | { 9 | T 1 10 | T_hist 0 11 | } 12 | Solidification 13 | { 14 | tSol 0 15 | G 0 16 | Gx 0 17 | Gy 0 18 | Gz 0 19 | V 0 20 | dTdt 0 21 | eqFrac 0 22 | depth 0 23 | numMelt 0 24 | RDF 0 25 | } 26 | Solidification+ 27 | { 28 | H 0 29 | Hx 0 30 | Hy 0 31 | Hz 0 32 | } -------------------------------------------------------------------------------- /examples/snapshot/Path.txt: -------------------------------------------------------------------------------- 1 | Mode X(mm) Y(mm) Z(mm) Pmod Time(s) 2 | 1 0 0 0 0 1.00E-04 3 | 0 5 0 0 1 1 4 | 0 5 1 0 1 1 5 | 1 4.5 1 0 1 5.00E-04 6 | 1 4 1 0 1 5.00E-04 7 | 1 3.5 1 0 1 5.00E-04 8 | 1 3 1 0 1 5.00E-04 9 | 1 2.5 1 0 1 5.00E-04 10 | 1 2 1 0 1 5.00E-04 11 | 1 1.5 1 0 1 5.00E-04 12 | 1 1 1 0 1 5.00E-04 13 | 1 0.5 1 0 1 5.00E-04 14 | 1 0 0 0 0 1.00E-04 15 | 16 | 17 | -------------------------------------------------------------------------------- /examples/snapshot_T_hist/Output.txt: -------------------------------------------------------------------------------- 1 | Grid 2 | { 3 | x 1 4 | y 1 5 | z 1 6 | } 7 | Temperature 8 | { 9 | T 1 10 | T_hist 1 11 | } 12 | Solidification 13 | { 14 | tSol 0 15 | G 0 16 | Gx 0 17 | Gy 0 18 | Gz 0 19 | V 0 20 | dTdt 0 21 | eqFrac 0 22 | depth 0 23 | numMelt 0 24 | RDF 0 25 | } 26 | Solidification+ 27 | { 28 | H 0 29 | Hx 0 30 | Hy 0 31 | Hz 0 32 | } -------------------------------------------------------------------------------- /examples/snapshot_T_hist/Path.txt: -------------------------------------------------------------------------------- 1 | Mode X(mm) Y(mm) Z(mm) Pmod Time(s) 2 | 1 0 0 0 0 1.00E-04 3 | 0 5 0 0 1 1 4 | 0 5 1 0 1 1 5 | 1 4.5 1 0 1 5.00E-04 6 | 1 4 1 0 1 5.00E-04 7 | 1 3.5 1 0 1 5.00E-04 8 | 1 3 1 0 1 5.00E-04 9 | 1 2.5 1 0 1 5.00E-04 10 | 1 2 1 0 1 5.00E-04 11 | 1 1.5 1 0 1 5.00E-04 12 | 1 1 1 0 1 5.00E-04 13 | 1 0.5 1 0 1 5.00E-04 14 | 1 0 0 0 0 1.00E-04 15 | 16 | 17 | -------------------------------------------------------------------------------- /examples/solidification/Path.txt: -------------------------------------------------------------------------------- 1 | Mode X(mm) Y(mm) Z(mm) Pmod Time(s) 2 | 1 0 0 0 0 1.00E-04 3 | 0 5 0 0 1 1 4 | 0 5 1 0 1 1 5 | 1 4.5 1 0 1 5.00E-04 6 | 1 4 1 0 1 5.00E-04 7 | 1 3.5 1 0 1 5.00E-04 8 | 1 3 1 0 1 5.00E-04 9 | 1 2.5 1 0 1 5.00E-04 10 | 1 2 1 0 1 5.00E-04 11 | 1 1.5 1 0 1 5.00E-04 12 | 1 1 1 0 1 5.00E-04 13 | 1 0.5 1 0 1 5.00E-04 14 | 1 0 0 0 0 1.00E-04 15 | 16 | 17 | -------------------------------------------------------------------------------- /examples/solidification_multibeam/Output.txt: -------------------------------------------------------------------------------- 1 | Grid 2 | { 3 | x 1 4 | y 1 5 | z 1 6 | } 7 | Temperature 8 | { 9 | T 0 10 | T_hist 0 11 | } 12 | Solidification 13 | { 14 | tSol 1 15 | G 1 16 | Gx 0 17 | Gy 0 18 | Gz 0 19 | V 1 20 | dTdt 0 21 | eqFrac 0 22 | depth 0 23 | numMelt 0 24 | RDF 0 25 | } 26 | Solidification+ 27 | { 28 | H 0 29 | Hx 0 30 | Hy 0 31 | Hz 0 32 | } -------------------------------------------------------------------------------- /examples/solidification_mpstats/Output.txt: -------------------------------------------------------------------------------- 1 | Grid 2 | { 3 | x 1 4 | y 1 5 | z 1 6 | } 7 | Temperature 8 | { 9 | T 0 10 | T_hist 0 11 | } 12 | Solidification 13 | { 14 | tSol 0 15 | G 0 16 | Gx 0 17 | Gy 0 18 | Gz 0 19 | V 0 20 | dTdt 0 21 | eqFrac 0 22 | depth 0 23 | numMelt 0 24 | RDF 0 25 | MP_Stats 1 26 | } 27 | Solidification+ 28 | { 29 | H 0 30 | Hx 0 31 | Hy 0 32 | Hz 0 33 | } -------------------------------------------------------------------------------- /examples/solidification/Output.txt: -------------------------------------------------------------------------------- 1 | Grid 2 | { 3 | x 1 4 | y 1 5 | z 1 6 | } 7 | Temperature 8 | { 9 | T 0 10 | T_hist 0 11 | } 12 | Solidification 13 | { 14 | tSol 0 15 | G 1 16 | Gx 0 17 | Gy 0 18 | Gz 0 19 | V 1 20 | dTdt 0 21 | eqFrac 0 22 | depth 0 23 | numMelt 0 24 | RDF 0 25 | MP_Stats 0 26 | } 27 | Solidification+ 28 | { 29 | H 0 30 | Hx 0 31 | Hy 0 32 | Hz 0 33 | } 34 | -------------------------------------------------------------------------------- /examples/solidification_stork/Output.txt: -------------------------------------------------------------------------------- 1 | Grid 2 | { 3 | x 1 4 | y 1 5 | z 1 6 | } 7 | Temperature 8 | { 9 | T 0 10 | T_hist 0 11 | } 12 | Solidification 13 | { 14 | tSol 0 15 | G 0 16 | Gx 0 17 | Gy 0 18 | Gz 0 19 | V 0 20 | dTdt 0 21 | eqFrac 0 22 | depth 0 23 | numMelt 0 24 | RDF 1 25 | MP_Stats 0 26 | } 27 | Solidification+ 28 | { 29 | H 0 30 | Hx 0 31 | Hy 0 32 | Hz 0 33 | } 34 | -------------------------------------------------------------------------------- /CITATION.bib: -------------------------------------------------------------------------------- 1 | @article{3dthesis, 2 | title = {An adaptive integration scheme for heat conduction in additive manufacturing}, 3 | journal = {Applied Mathematical Modelling}, 4 | volume = {75}, 5 | pages = {787-805}, 6 | year = {2019}, 7 | issn = {0307-904X}, 8 | doi = {https://doi.org/10.1016/j.apm.2019.07.008}, 9 | url = {https://www.sciencedirect.com/science/article/pii/S0307904X19304093}, 10 | author = {B. Stump and A. Plotkowski}, 11 | keywords = {Heat conduction, Numerical integration, Additive manufacturing, Solidification}, 12 | } -------------------------------------------------------------------------------- /makefile: -------------------------------------------------------------------------------- 1 | CXX := -g++ 2 | APP_NAME := 3DThesis 3 | 4 | BUILD := ./build 5 | OBJ_DIR := $(BUILD)/objects 6 | APP_DIR := $(BUILD)/application 7 | 8 | CXXFLAGS := -fopenmp -Ofast 9 | INCLUDE := -Iinclude/ 10 | 11 | SRC := $(wildcard src/*.cpp) 12 | OBJECTS := $(SRC:%.cpp=$(OBJ_DIR)/%.o) 13 | 14 | $(OBJ_DIR)/%.o: %.cpp 15 | @mkdir -p $(@D) 16 | $(CXX) $(CXXFLAGS) $(INCLUDE) -o $@ -c $< 17 | 18 | $(APP_DIR)/$(TARGET): $(OBJECTS) 19 | @mkdir -p $(@D) 20 | $(CXX) $(CXXFLAGS) $(INCLUDE) -o $(APP_DIR)/$(APP_NAME) $(OBJECTS) 21 | 22 | all: build $(APP_DIR)/$(APP_NAME) 23 | 24 | build: 25 | @mkdir -p $(APP_DIR) 26 | @mkdir -p $(OBJ_DIR) 27 | 28 | clean: 29 | -@rm -rvf $(OBJ_DIR)/* 30 | -@rm -rvf $(APP_DIR)/* 31 | -------------------------------------------------------------------------------- /CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 3.9) 2 | 3 | project(Thesis CXX) 4 | set(PROJECT_VERSION "4.1.0-dev") 5 | 6 | include(GNUInstallDirs) 7 | 8 | find_package(OpenMP) 9 | 10 | find_package(MPI QUIET COMPONENTS CXX) 11 | option(Thesis_REQUIRE_MPI "Require Thesis to build with MPI support" MPI_FOUND) 12 | if(Thesis_REQUIRE_MPI) 13 | find_package(MPI REQUIRED COMPONENTS CXX) 14 | endif() 15 | set(Thesis_ENABLE_MPI ${MPI_FOUND}) 16 | 17 | find_package(Git) 18 | if(GIT_FOUND AND IS_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}/.git) 19 | execute_process( 20 | COMMAND ${GIT_EXECUTABLE} log --pretty=format:%H -n 1 21 | WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR} 22 | OUTPUT_VARIABLE Thesis_COMMIT_HASH) 23 | else() 24 | set(Thesis_COMMIT_HASH "Not a git repository") 25 | endif() 26 | message(STATUS "Thesis version: ${PROJECT_VERSION}") 27 | message(STATUS "Thesis commit: ${Thesis_COMMIT_HASH}") 28 | 29 | add_subdirectory(src) 30 | add_subdirectory(bin) 31 | -------------------------------------------------------------------------------- /src/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | configure_file(ThesisConfig.h.cmakein ThesisConfig.h) 2 | 3 | set(THESIS_HEADERS 4 | Calc.h 5 | DataStructs.h 6 | Grid.h 7 | Init.h 8 | Melt.h 9 | Out.h 10 | Run.h 11 | Util.h 12 | ) 13 | if(Thesis_ENABLE_MPI) 14 | list(APPEND THESIS_HEADERS MpiStructs.h) 15 | endif() 16 | 17 | file(GLOB THESIS_SOURCES GLOB *.cpp) 18 | 19 | install(FILES ${THESIS_HEADERS} DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}) 20 | 21 | add_library(Thesis STATIC ${THESIS_SOURCES} ${THESIS_HEADERS}) 22 | 23 | target_include_directories(Thesis PUBLIC 24 | $ 25 | $ 26 | $) 27 | 28 | install(TARGETS Thesis 29 | EXPORT Thesis_Targets 30 | LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR} 31 | ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR}) 32 | 33 | add_library(Thesis::Thesis ALIAS Thesis) 34 | target_compile_features(Thesis PUBLIC cxx_std_11) 35 | target_link_libraries(Thesis PUBLIC OpenMP::OpenMP_CXX) 36 | 37 | if(Thesis_ENABLE_MPI) 38 | target_link_libraries(Thesis PUBLIC MPI::MPI_CXX) 39 | endif() -------------------------------------------------------------------------------- /BUILD.md: -------------------------------------------------------------------------------- 1 | 2 | ## Dependencies 3 | 3DThesis has the following dependencies: 4 | 5 | |Dependency | Version | Required | Details| 6 | |---------- | ------- |-------- |------- | 7 | |CMake | 3.9+ | No | Build system 8 | |OpenMP | - | Yes | Parallel threading 9 | 10 | ## Build and install 11 | 12 | ### CMake 13 | 14 | The following script will configure, build, and install 3DThesis. The only flag passed to CMake creates a local install: 15 | ``` 16 | cd 3DThesis 17 | mkdir build 18 | cd build 19 | cmake \ 20 | -D CMAKE_INSTALL_PREFIX=install \ 21 | .. 22 | make -j install 23 | ``` 24 | 25 | The following example adds more optional CMake flags to further customize the build: 26 | ``` 27 | cd 3DThesis 28 | mkdir build 29 | cd build 30 | cmake \ 31 | -D CMAKE_BUILD_TYPE="Release" \ 32 | -D CMAKE_CXX_COMPILER=g++ \ 33 | -D CMAKE_CXX_FLAGS="-Wall -Wextra -pedantic" \ 34 | -D CMAKE_INSTALL_PREFIX=install \ 35 | -D CMAKE_PREFIX_PATH="$OMP_INSTALL" \ 36 | .. 37 | make -j install 38 | ``` 39 | 40 | ### make 41 | 42 | An example makefile is also supported. To perform an in-source build, simply type `make` 43 | from the 3DThesis directory. Compiler settings, build flags, and build location can all 44 | be changed within that file. 45 | -------------------------------------------------------------------------------- /src/Out.h: -------------------------------------------------------------------------------- 1 | /**************************************************************************** 2 | * Copyright (c) 2019 UT-Battelle, LLC * 3 | * All rights reserved. * 4 | * * 5 | * This file is part of 3dThesis. 3dThesis is distributed under a * 6 | * BSD 3-clause license. For the licensing terms see the LICENSE file in * 7 | * the top-level directory. * 8 | * * 9 | * SPDX-License-Identifier: BSD-3-Clause * 10 | ****************************************************************************/ 11 | 12 | #pragma once 13 | #include "DataStructs.h" 14 | #include "Grid.h" 15 | 16 | #include 17 | #include 18 | #include 19 | 20 | using std::vector; 21 | using std::string; 22 | 23 | namespace Out { 24 | // Writes the progress to the console 25 | void Progress(const Simdat&, const int); 26 | // Writes the progress of points to the console 27 | void Point_Progress(const Simdat&, const int); 28 | 29 | // Functions for printing the version. 30 | std::string version(); 31 | std::string commitHash(); 32 | } 33 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | BSD 3-Clause License 2 | 3 | Copyright 2019 UT-Battelle, LLC 4 | All rights reserved. 5 | 6 | Redistribution and use in source and binary forms, with or without 7 | modification, are permitted provided that the following conditions are met: 8 | 9 | 1. Redistributions of source code must retain the above copyright notice, this 10 | list of conditions and the following disclaimer. 11 | 12 | 2. Redistributions in binary form must reproduce the above copyright notice, 13 | this list of conditions and the following disclaimer in the documentation 14 | and/or other materials provided with the distribution. 15 | 16 | 3. Neither the name of the copyright holder nor the names of its 17 | contributors may be used to endorse or promote products derived from 18 | this software without specific prior written permission. 19 | 20 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 21 | AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 22 | IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 23 | DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE 24 | FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 25 | DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 26 | SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER 27 | CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 28 | OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 29 | OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 30 | -------------------------------------------------------------------------------- /src/Calc.h: -------------------------------------------------------------------------------- 1 | /**************************************************************************** 2 | * Copyright (c) 2019 UT-Battelle, LLC * 3 | * All rights reserved. * 4 | * * 5 | * This file is part of 3dThesis. 3dThesis is distributed under a * 6 | * BSD 3-clause license. For the licensing terms see the LICENSE file in * 7 | * the top-level directory. * 8 | * * 9 | * SPDX-License-Identifier: BSD-3-Clause * 10 | ****************************************************************************/ 11 | 12 | #pragma once 13 | #include 14 | #include "DataStructs.h" 15 | 16 | using std::vector; 17 | using std::string; 18 | 19 | namespace Calc { 20 | // Get quadrature nodes and points for several subsequent timesteps in parallel 21 | void Integrate_Parallel(Nodes&, const Simdat&, const double, const bool); 22 | // Get quadrature nodes and points for one time 23 | void Integrate_Serial(Nodes&, const Simdat&, const double, const bool); 24 | 25 | // Adaptive Integration Scheme 26 | void GaussIntegrate(Nodes&, const Simdat&, const double, const bool); 27 | // Adaptive Integration Scheme with a compression scheme between neighboring path segments. Very usefull for point rasters. 28 | void GaussCompressIntegrate(Nodes&, const Simdat&, const double, const bool); 29 | 30 | // Adds Simple boundary conditions (x and y) via method of images 31 | void AddBCs(Nodes&, const Domain&); 32 | } -------------------------------------------------------------------------------- /src/Run.h: -------------------------------------------------------------------------------- 1 | /**************************************************************************** 2 | * Copyright (c) 2019 UT-Battelle, LLC * 3 | * All rights reserved. * 4 | * * 5 | * This file is part of 3dThesis. 3dThesis is distributed under a * 6 | * BSD 3-clause license. For the licensing terms see the LICENSE file in * 7 | * the top-level directory. * 8 | * * 9 | * SPDX-License-Identifier: BSD-3-Clause * 10 | ****************************************************************************/ 11 | 12 | #pragma once 13 | 14 | #include 15 | #include 16 | 17 | #include 18 | #include 19 | 20 | #include 21 | #include 22 | #include "DataStructs.h" 23 | #include "Grid.h" 24 | 25 | using std::vector; 26 | using std::string; 27 | using std::atomic; 28 | 29 | namespace Run{ 30 | //Chooses between modes 31 | void Simulate(Grid&, const Simdat&); 32 | 33 | //Choose between snapshot modes 34 | void Snapshots(Grid&, const Simdat&); 35 | 36 | //Snapshot modes 37 | void Snapshots_NoTracking(Grid&, const Simdat&); 38 | void Snapshots_Volume(Grid&, const Simdat&); 39 | void Snapshots_GeometryBounds(Grid&, const Simdat&); 40 | 41 | //Choose between solidification modes 42 | void Solidify(Grid&, const Simdat&); 43 | 44 | //Solidification Modes 45 | void Solidify_NoTracking(Grid&, const Simdat&); 46 | void Solidify_Volume(Grid&, const Simdat&); 47 | void Solidify_Surface(Grid&, const Simdat&); 48 | 49 | //Special Mode for Stork Data 50 | void Stork(Grid&, const Simdat&); 51 | } -------------------------------------------------------------------------------- /src/Out.cpp: -------------------------------------------------------------------------------- 1 | /**************************************************************************** 2 | * Copyright (c) 2019 UT-Battelle, LLC * 3 | * All rights reserved. * 4 | * * 5 | * This file is part of 3dThesis. 3dThesis is distributed under a * 6 | * BSD 3-clause license. For the licensing terms see the LICENSE file in * 7 | * the top-level directory. * 8 | * * 9 | * SPDX-License-Identifier: BSD-3-Clause * 10 | ****************************************************************************/ 11 | 12 | #include 13 | #include 14 | 15 | #include 16 | #include 17 | #include 18 | 19 | #include "Out.h" 20 | #include "DataStructs.h" 21 | #include "ThesisConfig.h" 22 | 23 | void Out::Progress(const Simdat& sim, const int itert) { 24 | static int prog_print_last = 0; 25 | int prog_now = int(10.0 * itert * sim.param.dt / sim.util.allScansEndTime); 26 | if (sim.print && prog_now != prog_print_last) { 27 | prog_print_last = prog_now; 28 | if (prog_print_last <= 10) { 29 | std::cout << "Time step: " << itert << "\t\t"; 30 | std::cout << "% of Path: " << 10 * prog_print_last << "%" << "\n"; 31 | } 32 | else{ 33 | std::cout << "Time step: " << itert << "\t\t"; 34 | std::cout << "Cooling... \n"; 35 | } 36 | } 37 | return; 38 | } 39 | 40 | void Out::Point_Progress(const Simdat& sim, const int p) { 41 | static int prog_print_last = 0; 42 | int prog_now = 10 * p / sim.domain.pnum; 43 | if (sim.print && prog_now != prog_print_last) { 44 | prog_print_last = prog_now; 45 | std::cout << "% of Points: " << 10 * prog_print_last << "%" << "\n"; 46 | } 47 | return; 48 | } 49 | 50 | std::string Out::version() { return Thesis_VERSION; } 51 | std::string Out::commitHash() { return Thesis_COMMIT_HASH; } 52 | -------------------------------------------------------------------------------- /src/Melt.h: -------------------------------------------------------------------------------- 1 | /**************************************************************************** 2 | * Copyright (c) 2019 UT-Battelle, LLC * 3 | * All rights reserved. * 4 | * * 5 | * This file is part of 3dThesis. 3dThesis is distributed under a * 6 | * BSD 3-clause license. For the licensing terms see the LICENSE file in * 7 | * the top-level directory. * 8 | * * 9 | * SPDX-License-Identifier: BSD-3-Clause * 10 | ****************************************************************************/ 11 | 12 | #pragma once 13 | #include "DataStructs.h" 14 | #include "Grid.h" 15 | #include "Util.h" 16 | 17 | using std::vector; 18 | using std::string; 19 | 20 | namespace Melt { 21 | // Adds relevant points to check if they are melted or not based on the beam path 22 | void beam_trace(vector&, Grid&, const Simdat&, const double, const double); 23 | // Checks the neigbors of points to see if they are melted too 24 | void neighbor_check(vector&, vector& , vector& , Grid&, vector&, const Nodes&, const Simdat&, const double, const bool); 25 | // Calculate the depth of a melt at a specific x,y coordinate. Only used in mode_3. 26 | void calc_depth(vector&, vector&, vector&, Grid&, const Nodes&, const Nodes&, const Simdat&, const double); 27 | // Calculate the max depth of a melt at a specific x,y coordinate. Only works in mode_3. 28 | void calc_depth_max(vector&, vector&, vector&, Grid&, const Nodes&, const Simdat&); 29 | // Calculate meltpool statistics 30 | // NOTE:: Will not work well when spatially decomposed 31 | // NOTE:: Will not work well with multiple beams 32 | void calc_mp_info(const vector&, Grid&, const Simdat&, const double); 33 | void local_neighbor_check(vector&, vector&, Grid&,const Simdat&); 34 | } 35 | 36 | -------------------------------------------------------------------------------- /examples/run_examples.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | # Execute from this script's directory 4 | script_dir=$(realpath $(dirname $0)) 5 | cd $script_dir 6 | declare -a status_array 7 | 8 | # Declase which tests to run 9 | declare -a example_cases=("snapshot" "solidification_mpstats" "solidification_multibeam" "snapshot_T_hist") 10 | 11 | # Loop through tests without MPI 12 | echo 13 | echo Running tests without MPI: 14 | echo 15 | executable="$script_dir/../install/bin/3DThesis" 16 | for case_dir in "${example_cases[@]}"; do 17 | 18 | # Change to case directory and clean any old files 19 | echo "- $case_dir" 20 | cd $case_dir 21 | [ -d Data ] && rm -r Data 22 | [ -f test.log ] && rm test.log 23 | [ -f 0 ] && rm 0 24 | 25 | # Execute the case and check that output files exist 26 | status=1 27 | status_text="Execution Not Successful" 28 | $executable ./ParamInput.txt > test.log 2>&1 29 | if [ -d Data ]; then 30 | file_count="$(find Data -name "$(basename $case_dir)*.csv" -printf "." | wc -m)" 31 | if [ $(expr $file_count) > 0 ]; then 32 | status_text="Output File Exists" 33 | status=0 34 | fi 35 | fi 36 | echo " - Test Status: $status_text ($status)" 37 | status_array+=($status) 38 | cd $script_dir 39 | done 40 | 41 | # Loop through tests with MPI 42 | echo 43 | echo Running tests with MPI: 44 | echo 45 | executable="mpirun -n 2 --oversubscribe $script_dir/../install/bin/3DThesis" 46 | for case_dir in "${example_cases[@]}"; do 47 | 48 | # Change to case directory and clean any old files 49 | echo "- $case_dir" 50 | cd $case_dir 51 | [ -d Data ] && rm -r Data 52 | [ -f test.log ] && rm test.log 53 | [ -f 0 ] && rm 0 # this file gets created sometimes by the execute command 54 | 55 | # Execute the case and check that output files exist 56 | status=1 57 | status_text="Execution Not Successful" 58 | $executable ./ParamInput.txt > test.log 2>&1 59 | if [ -d Data ]; then 60 | file_count="$(find Data -name "$(basename $case_dir)*.csv" -printf "." | wc -m)" 61 | if [ $(expr $file_count) > 0 ]; then 62 | status_text="Output File Exists" 63 | status=0 64 | fi 65 | fi 66 | echo " - Test Status: $status_text ($status)" 67 | status_array+=($status) 68 | cd $script_dir 69 | done 70 | 71 | # Output overall summary of tests 72 | echo 73 | echo "Task Status Summary:" 74 | if [[ "${status_array[*]}" =~ 1 ]]; then 75 | echo Fail 76 | else 77 | echo Pass 78 | fi -------------------------------------------------------------------------------- /src/Util.h: -------------------------------------------------------------------------------- 1 | /**************************************************************************** 2 | * Copyright (c) 2019 UT-Battelle, LLC * 3 | * All rights reserved. * 4 | * * 5 | * This file is part of 3dThesis. 3dThesis is distributed under a * 6 | * BSD 3-clause license. For the licensing terms see the LICENSE file in * 7 | * the top-level directory. * 8 | * * 9 | * SPDX-License-Identifier: BSD-3-Clause * 10 | ****************************************************************************/ 11 | 12 | #pragma once 13 | #include 14 | #include 15 | #include 16 | #include 17 | #include 18 | #include "DataStructs.h" 19 | 20 | using std::vector; 21 | using std::array; 22 | using std::string; 23 | using std::max; 24 | 25 | namespace Util { 26 | // Turns an integer into a zero-padded string 27 | string ZeroPadNumber(const int); 28 | string ZeroPadNumber(const int, const int); 29 | // Turns ijk indices into global point number 30 | inline int ijk_to_p(const int i, const int j, const int k, const Simdat& sim) { 31 | return i * (sim.domain.znum * sim.domain.ynum) + j * sim.domain.znum + k; 32 | } 33 | // Initializes the locks. They make sure only one thread can access a point at a time 34 | void SetLocks(vector&, const Simdat&); 35 | // Function to easily add integration segment to nodes 36 | void AddToNodes(Nodes&, const int_seg); 37 | // Function to combine nodes 38 | void CombineNodes(Nodes&, const Nodes&); 39 | // Function to cleat all nodes 40 | void ClearNodes(Nodes&); 41 | // Checks if it is inside the maximum radius 42 | bool InRMax(const double, const double, const Domain&, const Settings&); 43 | // Calculates the time to integrate back to 44 | double t0calc(const double, const Beam&, const Material&, const Settings&); 45 | // Gets the maximum allowable step size for a path segment 46 | double GetRefTime(const double, const int, const vector&, const Beam&); 47 | // Finds the current beam location 48 | int_seg GetBeamLoc(const double, const int, const vector&, const Simdat&); 49 | // Indicates when all points have solidified and the full scan is over 50 | bool sim_finish(const double, const Simdat&, const int); 51 | 52 | // Calculates scan end time 53 | void Calc_AllScansEndTime(Simdat&); 54 | // Calculates bounds of scan path 55 | void Calc_ScanBounds(Domain&, const vector>&); 56 | // Calculates the nondimensional integration time 57 | void Calc_NonD_dt(vector&, const Material&); 58 | // Calculates the maximum radius around the domain to be considered 59 | void Calc_RMax(Simdat&); 60 | 61 | // Rotates melt pool based on scan angle 62 | vector> rotateField(const vector>&, double, const int, const int); 63 | // Calculates min and max elements of a specific vector in the df 64 | double getMin(const vector>&, int); 65 | double getMax(const vector>&, int); 66 | // Calculates length, width and origin of melt pool 67 | array getLengthWidthOrigin(const vector>&, double, const int, const int); 68 | // Calculates percentage of the melt pool box that is melted 69 | double getPerBoxMelted(const vector>&, double, double, double); 70 | } 71 | -------------------------------------------------------------------------------- /src/Init.h: -------------------------------------------------------------------------------- 1 | /**************************************************************************** 2 | * Copyright (c) 2019 UT-Battelle, LLC * 3 | * All rights reserved. * 4 | * * 5 | * This file is part of 3dThesis. 3dThesis is distributed under a * 6 | * BSD 3-clause license. For the licensing terms see the LICENSE file in * 7 | * the top-level directory. * 8 | * * 9 | * SPDX-License-Identifier: BSD-3-Clause * 10 | ****************************************************************************/ 11 | 12 | #pragma once 13 | #include 14 | #include 15 | #include 16 | #include 17 | #include 18 | 19 | #include "DataStructs.h" 20 | 21 | using std::vector; 22 | using std::string; 23 | using std::stringstream; 24 | using std::to_string; 25 | 26 | namespace Init { 27 | // Are 1st level Keywords in the file 28 | void Keywords_Lv1(vector&, vector&, const string&); 29 | 30 | // Read for Keywords 31 | void Keywords_Lv2(vector&, vector>&, vector>&, const string&, const bool); 32 | void Keywords_Lv2(vector&, vector>&, vector>&, const string&, const bool); 33 | void catchPrint(const string&, const int, const bool); 34 | 35 | // Helps Set Values and Display Errors 36 | template 37 | void setPrint(Default simDefault, string name, const int err, const bool print){ 38 | if (print) { 39 | if (err == 1) 40 | std::cout << "Error: " << name << " not read. Setting to default value " << simDefault << std::endl; 41 | if (err == 2) 42 | std::cout << "Fatal Error: " << name << " not read" << std::endl; 43 | } 44 | } 45 | 46 | void SetValues(string&, string, string, string, int, const bool); 47 | void SetValues(int&, string, int, string, int, const bool); 48 | void SetValues(bool&, string, bool, string, int, const bool); 49 | void SetValues(double&, string, double, string, int, const bool); 50 | 51 | void checkAsterisks(const std::string, const std::string, const std::string, const bool); 52 | 53 | // Gets file names from the input file 54 | void GetFileNames(FileNames&, const string&, const bool); 55 | void MakeDataDirectory(const string&); 56 | void ReadSimParams(Simdat&); 57 | 58 | 59 | // Reads in simulation type information 60 | void FileRead_Mode(Simdat&, const string&); 61 | void FileRead_Mode_Snapshot(Simdat&, const string&); 62 | void FileRead_Mode_Solidification(Simdat&, const string&); 63 | 64 | // Reads in all necessary simulation parameters 65 | void FileRead_Material(Material&, const string&, const bool); 66 | void FileRead_Beam(Beam&, const string&, const bool); 67 | void FileRead_Path(vector&, const string&, const bool); 68 | 69 | // Reads in fine tuned simulation parameters 70 | void FileRead_Domain(Domain&, const string&, const bool); 71 | void FileRead_Output(Output&, const string&, const bool); 72 | void FileRead_Settings(Settings&, const string&, const bool); 73 | 74 | // Reads in utility files (miscellaneous stuff for research) 75 | void FileRead_Points(Domain&, const string&, const bool); 76 | 77 | // Set thermal diffusivity 78 | void SetDiffusivity(Material&); 79 | // Set beam efficiency 80 | void SetBeamPower(Beam&); 81 | // Initializes the positions of all points 82 | void SetDomainParams(Domain&); 83 | 84 | // For reading in multiple beams 85 | void FileRead_Beams(vector&, const string&, const bool); 86 | // For reading in multiple paths 87 | void FileRead_Paths(vector>&, const string&, const bool); 88 | } 89 | -------------------------------------------------------------------------------- /src/MpiStructs.h: -------------------------------------------------------------------------------- 1 | #ifndef THESIS_MPI_STRUCTS_H 2 | #define THESIS_MPI_STRUCTS_H 3 | 4 | #include 5 | #include 6 | 7 | #include "DataStructs.h" 8 | #include "Util.h" 9 | 10 | using std::string; 11 | 12 | class ThesisMPI{ 13 | private: 14 | // Communication Stuff 15 | MPI_Comm comm; 16 | 17 | // MPI info 18 | int rank; 19 | int nproc; 20 | 21 | // Locality info 22 | int i_min, i_max; 23 | int j_min, j_max; 24 | 25 | public: 26 | ThesisMPI(MPI_Comm inComm){ 27 | // Set Comm information 28 | comm = inComm; 29 | 30 | // Get MPI Info 31 | MPI_Comm_rank(comm, &rank); 32 | MPI_Comm_size(comm, &nproc); 33 | 34 | // Set mpi name 35 | name = Util::ZeroPadNumber(rank, 1+nproc/10); 36 | 37 | // TODO::CUSTOM DECOMPOSITION 38 | // Get dims for 2D decomposition 39 | MPI_Dims_create(nproc, 2, dims); 40 | 41 | // Create coms 42 | int periods[2] = {0,0}; // non-periodic boundaries 43 | MPI_Comm cart_comm; 44 | MPI_Cart_create(comm, 2, dims, periods, 0, &cart_comm); 45 | 46 | // Set coords to coordinate of local process 47 | MPI_Cart_coords(cart_comm, rank, 2, coords); 48 | } 49 | 50 | // Dimensionality of decomposition 51 | int dims[2] = {0,0}; // Dimensionality of decomposition 52 | int coords[2] = {0,0}; // Own coordinates 53 | 54 | // Name addition 55 | string name; 56 | 57 | int size() { return nproc; } 58 | 59 | void setPrint(Simdat& sim) { 60 | // Update local rank printing 61 | sim.print = rank == 0; 62 | sim.mpi = size() > 1; 63 | } 64 | 65 | // Make x-y bounds for local domain 66 | void makeLocalBounds(Simdat& sim){ 67 | // Global Decomposition 68 | const int I = dims[0]; 69 | const int J = dims[1]; 70 | 71 | // Local Decomposition Number 72 | const int i = coords[0]; 73 | const int j = coords[1]; 74 | 75 | // Find local domain bounds (no overlap for else) 76 | if (sim.param.mode=="Stork" || sim.settings.mpi_overlap){ 77 | // Find local domain bounds (overlap for stork) 78 | i_min = ((sim.domain.xnum-1)*i)/I; 79 | i_max = ((sim.domain.xnum-1)*(i+1))/I; 80 | 81 | j_min = ((sim.domain.ynum-1)*j)/J; 82 | j_max = ((sim.domain.ynum-1)*(j+1))/J; 83 | } 84 | else{ 85 | // Find local domain bounds (no overlap for else) 86 | i_min = ((sim.domain.xnum-1)*i)/I + (i!=0); 87 | i_max = ((sim.domain.xnum-1)*(i+1))/I; 88 | 89 | j_min = ((sim.domain.ynum-1)*j)/J + (j!=0); 90 | j_max = ((sim.domain.ynum-1)*(j+1))/J; 91 | } 92 | 93 | // Find local domain bounds (no) 94 | const double xmin = sim.domain.xmin + sim.domain.xres*i_min; 95 | const double xmax = sim.domain.xmin + sim.domain.xres*i_max; 96 | 97 | const double ymin = sim.domain.ymin + sim.domain.yres*j_min; 98 | const double ymax = sim.domain.ymin + sim.domain.yres*j_max; 99 | 100 | // Now adjust local domain 101 | sim.domain.xmin = xmin; sim.domain.xmax = xmax; 102 | sim.domain.xnum = 1 + int(0.5 + (sim.domain.xmax - sim.domain.xmin) / sim.domain.xres); 103 | sim.domain.xmax = sim.domain.xmin + (sim.domain.xnum - 1) * sim.domain.xres; 104 | 105 | sim.domain.ymin = ymin; sim.domain.ymax = ymax; 106 | sim.domain.ynum = 1 + int(0.5 + (sim.domain.ymax - sim.domain.ymin) / sim.domain.yres); 107 | sim.domain.ymax = sim.domain.ymin + (sim.domain.ynum - 1) * sim.domain.yres; 108 | 109 | sim.domain.pnum = sim.domain.xnum*sim.domain.ynum*sim.domain.znum; 110 | } 111 | }; 112 | 113 | 114 | #endif // THESIS_MPI_STRUCTS_H -------------------------------------------------------------------------------- /bin/Main.cpp: -------------------------------------------------------------------------------- 1 | /**************************************************************************** 2 | * Copyright (c) 2019 UT-Battelle, LLC * 3 | * All rights reserved. * 4 | * * 5 | * This file is part of 3dThesis. 3dThesis is distributed under a * 6 | * BSD 3-clause license. For the licensing terms see the LICENSE file in * 7 | * the top-level directory. * 8 | * * 9 | * SPDX-License-Identifier: BSD-3-Clause * 10 | ****************************************************************************/ 11 | 12 | #include 13 | #include 14 | #include 15 | #include 16 | 17 | #include "DataStructs.h" 18 | #include "Init.h" 19 | #include "Util.h" 20 | #include "Run.h" 21 | #include "Out.h" 22 | #include "Grid.h" 23 | #include "ThesisConfig.h" 24 | 25 | #ifdef Thesis_ENABLE_MPI 26 | #include "MpiStructs.h" 27 | #include 28 | #endif 29 | 30 | using std::vector; 31 | using std::string; 32 | 33 | using std::chrono::high_resolution_clock; 34 | using std::chrono::duration; 35 | 36 | // Read in file 37 | // File also has MPI parameters?? 38 | //// - Make MPI file? 39 | //// - Overlap option 40 | // Split file based on rank (spatially) 41 | // Run everything else normally 42 | // Output should say MPI information 43 | 44 | inline void run(int argc, char * argv[]) 45 | { 46 | // Start initialization clock 47 | auto start_in = high_resolution_clock::now(); 48 | 49 | //Command line input to program is the file that contains the simulation file names 50 | if (argc <= 1) { throw std::runtime_error( "Input file argument required: ./3DThesis ParamInput.txt" ); } 51 | string inputFile = argv[1]; 52 | 53 | // Initialize struct for simulation parameters 54 | Simdat sim; 55 | 56 | #ifdef Thesis_ENABLE_MPI 57 | // Initialize MPI 58 | ThesisMPI mpi(MPI_COMM_WORLD); 59 | mpi.setPrint(sim); 60 | #endif 61 | 62 | // Get names of intput files 63 | Init::GetFileNames(sim.files, inputFile, sim.print); 64 | 65 | // Read input files and set simulation parameters 66 | Init::ReadSimParams(sim); 67 | 68 | #ifdef Thesis_ENABLE_MPI 69 | // Make local bounds and set local rank 70 | mpi.makeLocalBounds(sim); 71 | #endif 72 | 73 | // Initialize grid 74 | Grid grid(sim); 75 | 76 | // MISC INIT STUFF 77 | sim.util.approxEndTime = sim.util.allScansEndTime; 78 | 79 | // Output initialization time 80 | auto stop_in = high_resolution_clock::now(); 81 | if (sim.print) 82 | std::cout << "Initialization time (s): " << (duration(stop_in - start_in).count())/1000.0 << "\n\n"; //(stop_in - start_in) / double(CLOCKS_PER_SEC) << "\n\n"; 83 | 84 | // Start simulation clock 85 | auto start_sim = high_resolution_clock::now(); 86 | 87 | // Run the simulation 88 | Run::Simulate(grid, sim); 89 | 90 | // Output simulation time 91 | auto stop_sim = high_resolution_clock::now(); 92 | if (sim.print) { 93 | std::cout << "Version: " << Out::version() << "\n"; 94 | std::cout << "Commit hash: " << Out::commitHash() << "\n"; 95 | std::cout << "Execution time (s): " << (duration(stop_sim - start_sim).count())/1000.0 << "\n\n";//(stop_sim - start_sim) / double(CLOCKS_PER_SEC) << "\n\n"; 96 | } 97 | // Start output clock 98 | auto start_out = high_resolution_clock::now(); 99 | 100 | std::string rank_name = ""; 101 | #ifdef Thesis_ENABLE_MPI 102 | if (sim.mpi) 103 | rank_name = "." + mpi.name; 104 | #endif 105 | 106 | if (sim.param.mode=="Solidification"){ 107 | if (sim.param.tracking!="Stork"){ 108 | grid.Output(sim, "Solidification.Final" + rank_name); 109 | } 110 | else{ 111 | grid.Output_RRDF_csv(sim, "RRDF" + rank_name); 112 | //grid.Output_RRDF_bin(sim, "RRDF" + rank_name); 113 | } 114 | } 115 | if (sim.output.T_hist) { 116 | grid.Output_T_hist(sim, "T.hist" + rank_name); 117 | } 118 | if (sim.output.RDF) { 119 | grid.Output_RDF(sim, "RDF.Final" + rank_name); 120 | } 121 | 122 | // Output output time 123 | auto stop_out = high_resolution_clock::now(); 124 | if (sim.print) 125 | std::cout << "Output time (s): " << (duration(stop_out - start_out).count()) / 1000.0 << "\n\n"; 126 | } 127 | 128 | #ifdef Thesis_ENABLE_MPI 129 | int main(int argc, char * argv[]) { 130 | // Initialize MPI 131 | MPI_Init(&argc, &argv); 132 | run(argc, argv); 133 | // Finalize MPI 134 | MPI_Finalize(); 135 | 136 | return 0; 137 | } 138 | #else 139 | int main(int argc, char * argv[]) { 140 | run(argc, argv); 141 | return 0; 142 | } 143 | #endif 144 | -------------------------------------------------------------------------------- /src/DataStructs.h: -------------------------------------------------------------------------------- 1 | /**************************************************************************** 2 | * Copyright (c) 2019 UT-Battelle, LLC * 3 | * All rights reserved. * 4 | * * 5 | * This file is part of 3dThesis. 3dThesis is distributed under a * 6 | * BSD 3-clause license. For the licensing terms see the LICENSE file in * 7 | * the top-level directory. * 8 | * * 9 | * SPDX-License-Identifier: BSD-3-Clause * 10 | ****************************************************************************/ 11 | 12 | #pragma once 13 | #include 14 | #include 15 | #include 16 | #include 17 | #include 18 | #include 19 | 20 | using std::vector; 21 | using std::string; 22 | using std::deque; 23 | using std::list; 24 | 25 | #define PI 3.14159265358979323846 26 | 27 | struct int_seg { 28 | double xb, yb, zb, phix, phiy, phiz, dtau, qmod; 29 | }; 30 | 31 | // What is used to integrate 32 | struct Nodes { 33 | size_t size = 0; 34 | // {xb,yb,zb} = coords 35 | // {phix,phiy,phiz} = diffusion 36 | // {dtau} = node weight 37 | // {expmod} = frontloads computation 38 | vector xb, yb, zb, phix, phiy, phiz, dtau, expmod; 39 | }; 40 | 41 | // What is read in from the paths 42 | struct path_seg{ 43 | int smode; //Segment mode (line melt | spot melt) 44 | double sx, sy, sz; //Segment end coordinates 45 | double sqmod; //Segment power modulation 46 | double sparam; //Segment time parameter (speed | spot time ) 47 | double seg_time; //Segment end time 48 | }; 49 | 50 | // 3D coordinate 51 | struct coord 52 | { 53 | double x, y, z; 54 | }; 55 | 56 | // Filename read into simulation 57 | struct FileNames { 58 | string name, dataDir, mode, material, beam, path; 59 | string domain, output, settings; 60 | }; 61 | 62 | // Material constants 63 | struct Material { 64 | double kon; // Thermal Conductivty 65 | double rho; // Density 66 | double cps; // Specifc Heat 67 | double T_liq; // Liquidus Temperature 68 | double T_init; // Inital Temperature (Preheat/Ambient) 69 | double a; // Thermal Diffusivity 70 | double cet_a, cet_n, cet_N0; // Parameters for CET 71 | }; 72 | 73 | // Beam specific parameters 74 | struct Beam { 75 | double ax, ay, az; // Beam Shape 76 | double eff; // Absoprtion Efficiency 77 | double q; // Beam Power 78 | double nond_dt; // Nondimensional Time 79 | }; 80 | 81 | // Collection of simulation parameters 82 | struct SimParams { 83 | // Mode 84 | string mode; // Snapshots, TemperatureHistory, Solidification 85 | 86 | // Snapshots 87 | vector SnapshotTimes; 88 | 89 | // Solidification 90 | string tracking = "None"; // meltpool tracking mode 91 | double dt = 1e-5; // timestep 92 | int out_freq = 1; // output frequency 93 | double radiusCheck = 1.0; // The radius to be checking perimeter points from (TODO::different behavior for >1 and 0) 94 | bool secondary = false; // calculate secondary solidifiaction 95 | }; 96 | 97 | // Domain paramters 98 | struct Domain { 99 | // Domain numbers 100 | int xnum, ynum, znum, pnum; 101 | 102 | // Domain bounds 103 | double xmin = DBL_MAX; 104 | double xmax = -DBL_MAX; 105 | double ymin = DBL_MAX; 106 | double ymax = -DBL_MAX; 107 | double zmin = DBL_MAX; 108 | double zmax = -DBL_MAX; 109 | 110 | // Domain resolution 111 | double xres, yres, zres; 112 | 113 | // Domain reflections 114 | bool use_BCs; 115 | int BC_reflections; 116 | double BC_xmin, BC_xmax, BC_ymin, BC_ymax, BC_zmin; 117 | 118 | // Domain point file 119 | string pointsFile; 120 | vector points; 121 | bool customPoints; 122 | }; 123 | 124 | // Fundamental settings 125 | struct Settings { 126 | 127 | // Itegration settings 128 | double dtau_min, t_hist, p_hist, r_max; 129 | 130 | // Liquidus search via Newton method settings 131 | int max_iter; 132 | double dttest; 133 | 134 | // Scan Path Compression 135 | int compress; 136 | 137 | // Compute 138 | int thnum; 139 | bool use_PINT; 140 | 141 | // MPI 142 | bool mpi_overlap; 143 | }; 144 | 145 | // Some utility variables 146 | struct Utility { 147 | double allScansEndTime = 0; // Time when all scans are done 148 | double approxEndTime = 0; // Approximate end time to simulation 149 | bool sol_finish = false; // Has solidification finished 150 | bool do_sol = false; // Do solidification calculation 151 | }; 152 | 153 | // What variables to output 154 | struct Output { 155 | bool x, y, z; 156 | bool T, T_hist; 157 | bool tSol, G, Gx, Gy, Gz, V, dTdt, eqFrac, depth, numMelt; 158 | bool RDF, mp_stats; 159 | bool H, Hx, Hy, Hz; 160 | }; 161 | 162 | // Entire simulation data structure 163 | struct Simdat{ 164 | FileNames files; 165 | Output output; 166 | 167 | Material material; 168 | 169 | vector beams; 170 | vector> paths; 171 | 172 | Domain domain; 173 | SimParams param; 174 | 175 | Settings settings; 176 | 177 | Utility util; 178 | 179 | // Should this rank print? 180 | bool print = true; 181 | // Is this running with MPI (actually using multiple ranks)? 182 | bool mpi = false; 183 | }; -------------------------------------------------------------------------------- /.github/workflows/CI.yml: -------------------------------------------------------------------------------- 1 | name: CI 2 | on: 3 | push: 4 | branches: 5 | - master 6 | pull_request: 7 | branches: 8 | - master 9 | 10 | concurrency: 11 | group: ${ {github.event_name }}-${{ github.workflow }}-${{ github.ref }} 12 | cancel-in-progress: ${{github.event_name == 'pull_request'}} 13 | 14 | jobs: 15 | CI: 16 | defaults: 17 | run: 18 | shell: bash 19 | strategy: 20 | matrix: 21 | distro: ['ubuntu:latest'] 22 | cxx: ['g++', 'clang++'] 23 | cmake_build_type: ['Debug', 'Release'] 24 | runs-on: ubuntu-latest 25 | container: ghcr.io/ornl-mdf/ci-containers/ubuntu:latest 26 | steps: 27 | - name: Checkout 3DThesis 28 | uses: actions/checkout@v3 29 | - name: Build 3DThesis 30 | run: | 31 | cmake -B build \ 32 | -DCMAKE_INSTALL_PREFIX=$HOME/3dthesis \ 33 | -DMPIEXEC_PREFLAGS="--oversubscribe" \ 34 | -DCMAKE_CXX_COMPILER=${{ matrix.cxx }} \ 35 | -DCMAKE_CXX_FLAGS="-Wall -pedantic ${cmake_cxx_flags[@]}" \ 36 | -DCMAKE_BUILD_TYPE=${{ matrix.cmake_build_type }} \ 37 | ${cmake_opts[@]} 38 | cmake --build build --parallel 2 39 | cmake --install build 40 | - name: Test 3DThesis Snapshots 41 | run: | 42 | cd $GITHUB_WORKSPACE/examples/snapshot 43 | echo Current working directory: $(pwd) 44 | $HOME/3dthesis/bin/3DThesis ./ParamInput.txt 45 | file_count="$(find Data -name "snapshot*.csv" -printf "." | wc -m)" 46 | if [ $(expr $file_count) < 1 ]; then 47 | exit 1 48 | fi 49 | rm -r Data 50 | mpirun -n 2 --oversubscribe $HOME/3dthesis/bin/3DThesis ./ParamInput.txt 51 | file_count="$(find Data -name "snapshot*.csv" -printf "." | wc -m)" 52 | if [ $(expr $file_count) < 1 ]; then 53 | exit 1 54 | fi 55 | - name: Test 3DThesis Snapshots T_hist 56 | run: | 57 | cd $GITHUB_WORKSPACE/examples/snapshot_T_hist 58 | echo Current working directory: $(pwd) 59 | $HOME/3dthesis/bin/3DThesis ./ParamInput.txt 60 | file_count="$(find Data -name "snapshot_T_hist*.csv" -printf "." | wc -m)" 61 | if [ $(expr $file_count) < 1 ]; then 62 | exit 1 63 | fi 64 | rm -r Data 65 | mpirun -n 2 --oversubscribe $HOME/3dthesis/bin/3DThesis ./ParamInput.txt 66 | file_count="$(find Data -name "snapshot_T_hist*.csv" -printf "." | wc -m)" 67 | if [ $(expr $file_count) < 1 ]; then 68 | exit 1 69 | fi 70 | - name: Test 3DThesis Solidification 71 | run: | 72 | cd $GITHUB_WORKSPACE/examples/solidification 73 | echo Current working directory: $(pwd) 74 | $HOME/3dthesis/bin/3DThesis ./ParamInput.txt 75 | file_count="$(find Data -name "solidification*.csv" -printf "." | wc -m)" 76 | if [ $(expr $file_count) < 1 ]; then 77 | exit 1 78 | fi 79 | rm -r Data 80 | mpirun -n 2 --oversubscribe $HOME/3dthesis/bin/3DThesis ./ParamInput.txt 81 | file_count="$(find Data -name "solidification*.csv" -printf "." | wc -m)" 82 | if [ $(expr $file_count) < 1 ]; then 83 | exit 1 84 | fi 85 | - name: Test 3DThesis Solidification MultiBeam 86 | run: | 87 | cd $GITHUB_WORKSPACE/examples/solidification_multibeam 88 | echo Current working directory: $(pwd) 89 | $HOME/3dthesis/bin/3DThesis ./ParamInput.txt 90 | file_count="$(find Data -name "solidification_multibeam*.csv" -printf "." | wc -m)" 91 | if [ $(expr $file_count) < 1 ]; then 92 | exit 1 93 | fi 94 | rm -r Data 95 | mpirun -n 2 --oversubscribe $HOME/3dthesis/bin/3DThesis ./ParamInput.txt 96 | file_count="$(find Data -name "solidification_multibeam*.csv" -printf "." | wc -m)" 97 | if [ $(expr $file_count) < 1 ]; then 98 | exit 1 99 | fi 100 | - name: Test 3DThesis Solidificiton with Meltpool Stats 101 | run: | 102 | cd $GITHUB_WORKSPACE/examples/solidification_mpstats 103 | echo Current working directory: $(pwd) 104 | $HOME/3dthesis/bin/3DThesis ./ParamInput.txt 105 | file_count="$(find Data -name "solidification_mpstats*.csv" -printf "." | wc -m)" 106 | if [ $(expr $file_count) < 1 ]; then 107 | exit 1 108 | fi 109 | rm -r Data 110 | mpirun -n 2 --oversubscribe $HOME/3dthesis/bin/3DThesis ./ParamInput.txt 111 | file_count="$(find Data -name "solidification_mpstats*.csv" -printf "." | wc -m)" 112 | if [ $(expr $file_count) < 1 ]; then 113 | exit 1 114 | fi 115 | - name: Test 3DThesis Solidification with Stork output 116 | run: | 117 | cd $GITHUB_WORKSPACE/examples/solidification_stork 118 | echo Current working directory: $(pwd) 119 | $HOME/3dthesis/bin/3DThesis ./ParamInput.txt 120 | file_count="$(find Data -name "stork.RRDF*.csv" -printf "." | wc -m)" 121 | if [ $(expr $file_count) < 1 ]; then 122 | exit 1 123 | fi 124 | rm -r Data 125 | mpirun -n 2 --oversubscribe $HOME/3dthesis/bin/3DThesis ./ParamInput.txt 126 | file_count="$(find Data -name "stork.RRDF*.csv" -printf "." | wc -m)" 127 | if [ $(expr $file_count) < 1 ]; then 128 | exit 1 129 | fi 130 | 131 | Windows: 132 | # Update the job name based on the compiler being tested 133 | name: Windows (${{ matrix.config.name }}) 134 | runs-on: windows-latest 135 | 136 | strategy: 137 | matrix: 138 | config: 139 | - name: MinGW 140 | compiler: mingw 141 | # For MinGW, specify the generator and compiler paths. 142 | cmake_generator: "Ninja" 143 | cxx_compiler: "C:/mingw64/bin/g++.exe" 144 | c_compiler: "C:/mingw64/bin/gcc.exe" 145 | 146 | - name: MSVC 147 | compiler: msvc 148 | # For MSVC, specify the Visual Studio generator. 149 | # The compiler is auto-detected by CMake. 150 | cmake_generator: "Visual Studio 17 2022" 151 | cxx_compiler: "" # Not needed 152 | c_compiler: "" # Not needed 153 | 154 | steps: 155 | - name: Checkout 3DThesis 156 | uses: actions/checkout@v4 157 | 158 | - name: Build 159 | shell: cmd 160 | # Set compiler paths for CMake in MinGW configuration. 161 | env: 162 | CXX: ${{ matrix.config.cxx_compiler }} 163 | CC: ${{ matrix.config.c_compiler }} 164 | # build using CMake with the generator from the matrix. 165 | # For MSVC, we must specify a build configuration (e.g., Release). 166 | # The '--config' flag is ignored by single-configuration generators like Ninja. 167 | run: | 168 | cmake -B build ^ 169 | -G "${{ matrix.config.cmake_generator }}" ^ 170 | -DCMAKE_INSTALL_PREFIX=3dthesis ^ 171 | -DMPIEXEC_PREFLAGS="--oversubscribe" 172 | 173 | cmake --build build --config Release --parallel 2 174 | cmake --install build --config Release 175 | 176 | - name: Test 3DThesis Snapshots 177 | shell: cmd 178 | run: | 179 | cd %GITHUB_WORKSPACE%\examples\snapshot 180 | echo Current working directory: %CD% 181 | 182 | rem The executable path is the same regardless of the compiler. 183 | %GITHUB_WORKSPACE%\3dthesis\bin\3DThesis.exe .\ParamInput.txt 184 | 185 | rem Check if the 'Data' directory exists. 186 | if not exist Data ( 187 | echo Error: The 'Data' directory was not created. 188 | exit /b 1 189 | ) 190 | 191 | rem Count the number of snapshot files. 192 | set file_count=0 193 | for /f %%i in ('dir /b /a-d Data\snapshot*.csv ^| find /c /v ""') do set file_count=%%i 194 | 195 | echo Found %file_count% snapshot files. 196 | 197 | rem Check if the file count is less than 1. 198 | if %file_count% LSS 1 ( 199 | echo Error: No snapshot files were found. 200 | exit /b 1 201 | ) 202 | 203 | rem Clean up the created directory. 204 | rmdir /s /q Data 205 | -------------------------------------------------------------------------------- /src/Util.cpp: -------------------------------------------------------------------------------- 1 | /**************************************************************************** 2 | * Copyright (c) 2019 UT-Battelle, LLC * 3 | * All rights reserved. * 4 | * * 5 | * This file is part of 3dThesis. 3dThesis is distributed under a * 6 | * BSD 3-clause license. For the licensing terms see the LICENSE file in * 7 | * the top-level directory. * 8 | * * 9 | * SPDX-License-Identifier: BSD-3-Clause * 10 | ****************************************************************************/ 11 | 12 | #include 13 | #include 14 | #include 15 | 16 | #include 17 | #include 18 | 19 | #include "Util.h" 20 | #include "Calc.h" 21 | #include "DataStructs.h" 22 | 23 | string Util::ZeroPadNumber(const int num) 24 | { 25 | return ZeroPadNumber(num, 7); 26 | } 27 | 28 | string Util::ZeroPadNumber(const int num, const int width) 29 | { 30 | std::ostringstream ss; 31 | ss << std::setw(width) << std::setfill('0') << num; 32 | return ss.str(); 33 | } 34 | 35 | void Util::SetLocks(vector& lock, const Simdat& sim) { 36 | for (int p = 0; p < lock.size(); p++) { omp_init_lock(&(lock[p])); } 37 | return; 38 | } 39 | 40 | void Util::AddToNodes(Nodes& nodes, const int_seg seg) { 41 | nodes.size++; 42 | nodes.xb.push_back(seg.xb); 43 | nodes.yb.push_back(seg.yb); 44 | nodes.zb.push_back(seg.zb); 45 | nodes.phix.push_back(1.0/seg.phix); // Precompute division 46 | nodes.phiy.push_back(1.0/seg.phiy); // Precompute division 47 | nodes.phiz.push_back(1.0/seg.phiz); // Precompute division 48 | nodes.dtau.push_back(seg.dtau); 49 | nodes.expmod.push_back(log(seg.qmod)-log(seg.phix * seg.phiy * seg.phiz) / 2.0); 50 | } 51 | 52 | void Util::CombineNodes(Nodes& nodes, const Nodes& nodes2) { 53 | nodes.size += nodes2.size; 54 | nodes.xb.insert(nodes.xb.end(), nodes2.xb.begin(), nodes2.xb.end()); 55 | nodes.yb.insert(nodes.yb.end(), nodes2.yb.begin(), nodes2.yb.end()); 56 | nodes.zb.insert(nodes.zb.end(), nodes2.zb.begin(), nodes2.zb.end()); 57 | nodes.phix.insert(nodes.phix.end(), nodes2.phix.begin(), nodes2.phix.end()); 58 | nodes.phiy.insert(nodes.phiy.end(), nodes2.phiy.begin(), nodes2.phiy.end()); 59 | nodes.phiz.insert(nodes.phiz.end(), nodes2.phiz.begin(), nodes2.phiz.end()); 60 | nodes.dtau.insert(nodes.dtau.end(), nodes2.dtau.begin(), nodes2.dtau.end()); 61 | nodes.expmod.insert(nodes.expmod.end(), nodes2.expmod.begin(), nodes2.expmod.end()); 62 | } 63 | 64 | void Util::ClearNodes(Nodes& nodes) { 65 | nodes.size = 0; 66 | nodes.xb.clear(); 67 | nodes.yb.clear(); 68 | nodes.zb.clear(); 69 | nodes.phix.clear(); 70 | nodes.phiy.clear(); 71 | nodes.phiz.clear(); 72 | nodes.dtau.clear(); 73 | nodes.expmod.clear(); 74 | } 75 | 76 | void Util::Calc_AllScansEndTime(Simdat& sim) { 77 | for (const vector& path : sim.paths) { 78 | sim.util.allScansEndTime = max(sim.util.allScansEndTime, path.back().seg_time); 79 | } 80 | } 81 | 82 | void Util::Calc_ScanBounds(Domain& domain, const vector>& paths) { 83 | double xmin = DBL_MAX; 84 | double xmax = -DBL_MAX; 85 | double ymin = DBL_MAX; 86 | double ymax = -DBL_MAX; 87 | double zmin = DBL_MAX; 88 | double zmax = -DBL_MAX; 89 | 90 | // Variables to store previous segment values 91 | double prev_sx = 0.0, prev_sy = 0.0, prev_sz = 0.0, prev_sqmod = 0.0; 92 | bool firstSegment = true; 93 | 94 | for (const vector& path : paths) { 95 | for (const path_seg& seg : path) { 96 | if (seg.sqmod > 0.0) { 97 | if (firstSegment) { 98 | xmin = xmax = seg.sx; 99 | ymin = ymax = seg.sy; 100 | zmin = zmax = seg.sz; 101 | } else { 102 | if (seg.sx < xmin) { xmin = seg.sx; } 103 | if (seg.sx > xmax) { xmax = seg.sx; } 104 | if (seg.sy < ymin) { ymin = seg.sy; } 105 | if (seg.sy > ymax) { ymax = seg.sy; } 106 | if (seg.sz < zmin) { zmin = seg.sz; } 107 | if (seg.sz > zmax) { zmax = seg.sz; } 108 | if ((prev_sqmod == 0.0) && (seg.smode == 0)) { 109 | if (prev_sx < xmin) { xmin = prev_sx; } 110 | if (prev_sx > xmax) { xmax = prev_sx; } 111 | if (prev_sy < ymin) { ymin = prev_sy; } 112 | if (prev_sy > ymax) { ymax = prev_sy; } 113 | if (prev_sz < zmin) { zmin = prev_sz; } 114 | if (prev_sz > zmax) { zmax = prev_sz; } 115 | } 116 | } 117 | } 118 | 119 | // Store the current segments values to check for initial 120 | // raster starting point (if Mode 0 and going from no power -> power) 121 | firstSegment = false; 122 | prev_sx = seg.sx; 123 | prev_sy = seg.sy; 124 | prev_sz = seg.sz; 125 | prev_sqmod = seg.sqmod; 126 | } 127 | } 128 | 129 | if (domain.xmin == -DBL_MAX) { domain.xmin = xmin - 500e-6; } 130 | if (domain.xmax == DBL_MAX) { domain.xmax = xmax + 500e-6; } 131 | if (domain.ymin == -DBL_MAX) { domain.ymin = ymin - 500e-6; } 132 | if (domain.ymax == DBL_MAX) { domain.ymax = ymax + 500e-6; } 133 | if (domain.zmin == -DBL_MAX) { domain.zmin = zmin - 250e-6; } 134 | if (domain.zmax == DBL_MAX) { domain.zmax = zmax; } 135 | 136 | } 137 | 138 | void Util::Calc_NonD_dt(vector& beams, const Material& material) { 139 | for (Beam& beam : beams) { 140 | beam.nond_dt = beam.ax * beam.ax / material.a; 141 | } 142 | return; 143 | } 144 | 145 | void Util::Calc_RMax (Simdat& sim){ 146 | sim.settings.t_hist = 1.0 / sim.settings.t_hist; 147 | if (sim.settings.r_max<0.0) { 148 | for (const Beam& beam : sim.beams) { 149 | //If the temperature never gets to 1/t_hist the peak temperature 150 | if (sim.settings.t_hist < exp(3.0 / 2.0)) { sim.settings.r_max = beam.ax * sqrt(log(sim.settings.t_hist) / 3.0); } 151 | else { sim.settings.r_max = beam.ax * pow(sim.settings.t_hist, (1.0 / 3.0)) / sqrt(2.0 * exp(1.0)); } 152 | //If the power never gets to x (K/s) 153 | double beta = pow(3.0 / 3.14159, 1.5) * beam.q / (sim.material.rho * sim.material.cps); 154 | double temp_diff = sim.material.T_liq - sim.material.T_init; 155 | double x = temp_diff * sim.settings.p_hist; 156 | double r_max_2; 157 | if (beta / (x * beam.ax * beam.ax * beam.ax) < exp(3.0 / 2.0)) { r_max_2 = beam.ax * sqrt(log(beta / (x * beam.ax * beam.ax * beam.ax)) / 3.0); } 158 | else { r_max_2 = pow(beta / x, (1.0 / 3.0)) / sqrt(2.0 * exp(1.0)); } 159 | 160 | //Choose the greater of the two 161 | sim.settings.r_max = max(sim.settings.r_max, r_max_2); 162 | //sim.r_max = sim.ax*pow(sim.t_hist, (1.0 / 3.0)) / sqrt(2.0*exp(1.0)); 163 | } 164 | } 165 | return; 166 | } 167 | 168 | bool Util::InRMax(const double x, const double y, const Domain& domain, const Settings& settings) { 169 | if ((x > (domain.xmax + settings.r_max)) || (x < (domain.xmin - settings.r_max))) {return false;} 170 | else if ((y >(domain.ymax + settings.r_max)) || (y < (domain.ymin - settings.r_max))) {return false; } 171 | else {return true;} 172 | } 173 | 174 | double Util::t0calc(const double t, const Beam& beam, const Material& material, const Settings& settings) { 175 | //Time for beam peak to decay to a fraction (t_hist) of it's initial power 176 | const double t_hist_t = beam.nond_dt / 12.0*(pow(settings.t_hist, (2.0 / 3.0)) - 1); 177 | 178 | //Time for beam to never exert more than a fraction (p_hist) of the difference between the preheat and solidus temperature 179 | const double beta = pow(3 / 3.14159, 1.5) * beam.q / (material.rho * material.cps); 180 | const double temp_diff = material.T_liq - material.T_init; 181 | const double x = temp_diff * settings.p_hist; 182 | const double y = 432.0*t*(x*x)*(material.a*material.a*material.a) / (beta*beta); 183 | const double p_hist_t = t / ((1.0 + sqrt(y))*(1.0 + sqrt(y))); 184 | 185 | double t0 = t - max(t_hist_t, p_hist_t); 186 | if (t0 < 0.0) { t0 = 0.0; } 187 | t0 = 0.0; 188 | 189 | return t0; 190 | } 191 | 192 | double Util::GetRefTime(const double tpp, const int seg, const vector& path, const Beam& beam) { 193 | 194 | double ref_t; 195 | const double spp = max(tpp / beam.nond_dt, 0.0); 196 | 197 | //Sets maximum time for line mode (derived from diffusion distance) 198 | if (path[seg].smode == 0) { 199 | double t0 = 0.58870501125 * beam.ax / path[seg].sparam; // sqrt(log(sqrt(2)))~0.58870501125 200 | ref_t = t0 * sqrt(12.0 * spp + 1.0); 201 | } 202 | //Sets maximum time for spot mode (equal to spot time) 203 | else if (path[seg].smode == 1) { 204 | ref_t = path[seg].seg_time - path[seg - 1].seg_time; 205 | if (ref_t == 0) {ref_t = 1.0e-9;} 206 | } 207 | 208 | return ref_t; 209 | } 210 | 211 | int_seg Util::GetBeamLoc(const double time, const int seg, const vector& path, const Simdat& sim) { 212 | 213 | int_seg current_seg; 214 | //Location calculation for spot mode 215 | if (path[seg].smode) { 216 | current_seg.xb = path[seg].sx; 217 | current_seg.yb = path[seg].sy; 218 | current_seg.zb = path[seg].sz; 219 | } 220 | //Location calculation for line mode 221 | else { 222 | const double dx = path[seg].sx - path[seg - 1].sx; 223 | const double dy = path[seg].sy - path[seg - 1].sy; 224 | const double dz = path[seg].sz - path[seg - 1].sz; 225 | const double tcur = time - path[seg - 1].seg_time; 226 | const double dt_cur = path[seg].seg_time - path[seg - 1].seg_time; 227 | current_seg.xb = path[seg - 1].sx + (tcur / dt_cur)*dx; 228 | current_seg.yb = path[seg - 1].sy + (tcur / dt_cur)*dy; 229 | current_seg.zb = path[seg - 1].sz + (tcur / dt_cur)*dz; 230 | } 231 | 232 | // If we are sufficiently outside the domain, set power to zero (so it won't be added to integration) 233 | if (Util::InRMax(current_seg.xb,current_seg.yb,sim.domain,sim.settings)){ current_seg.qmod = path[seg].sqmod; } 234 | else { current_seg.qmod = 0.0; } 235 | 236 | return current_seg; 237 | } 238 | 239 | bool Util::sim_finish(const double t, const Simdat& sim, const int liq_num) { 240 | bool isDone = false; 241 | if (t > sim.util.allScansEndTime && liq_num == 0) {isDone = true;} 242 | return isDone; 243 | } 244 | 245 | vector> Util::rotateField(const vector>& df, double angle, const int x, const int y){ 246 | vector> df_rot(df); 247 | for (size_t i = 0; i < df_rot.size(); i++){ 248 | df_rot[i][x] = df[i][x] * cos(-angle) - df[i][y] * sin(-angle); 249 | df_rot[i][y] = df[i][x] * sin(-angle) + df[i][y] * cos(-angle); 250 | } 251 | return df_rot; 252 | } 253 | 254 | double Util::getMax(const vector>& df, int index){ 255 | return (*max_element(df.begin(), df.end(), [index](const vector& a, const vector& b) { 256 | return a[index] < b[index]; 257 | }))[index]; 258 | 259 | } 260 | 261 | double Util::getMin(const vector>& df, int index) { 262 | return (*min_element(df.begin(), df.end(), [index](const vector& a, const vector& b) { 263 | return a[index] < b[index]; 264 | }))[index]; 265 | } 266 | 267 | std::array Util::getLengthWidthOrigin(const vector>& df, double resolution, const int x, const int y){ 268 | double length = Util::getMax(df, x) - Util::getMin(df, x) + resolution; 269 | double width = Util::getMax(df, y) - Util::getMin(df, y) + resolution; 270 | std::array origin = {getMin(df, x), getMin(df, y)}; 271 | std::array stats = {length, width, origin[0], origin[1]}; 272 | return stats; 273 | } 274 | 275 | 276 | double Util::getPerBoxMelted(const vector>& df, double length, double width, double resolution){ 277 | double melted_area = df.size() * pow(resolution, 2); 278 | double box_area = length * width; 279 | double per_box_melted = melted_area / box_area * 100; 280 | if (per_box_melted <= 100.0) {return per_box_melted;} 281 | else {return std::numeric_limits::quiet_NaN();} 282 | } 283 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # 3dThesis 2 | 3 | Heat transfer code utilizing a nondimensionalized semi-analytic solution to moving heat sources with a 3D Gaussian power density 4 | 5 | A detailed explanation of the mathematics can be found in [Stump and Plotkowski](CITATION.bib). 6 | 7 | ## Citing 8 | 9 | If you use 3dThesis in your work, please cite the [Stump and Plotkowski](CITATION.bib). 10 | The original release is available on [DOE Code](https://doi.org/10.11578/dc.20200909.3). Subsequent releases are updated on [Zenodo](https://doi.org/10.5281/zenodo.13686743). 11 | 12 | ## Acknowledgements 13 | 14 | 3dThesis has been authored by UT-Battelle, LLC under Contract No. DE-AC05-00OR22725 with the U.S. Department of Energy. 15 | 16 | 3dThesis was originally co-sponsored by the U.S. Department of Energy, Office of Energy Efficiency and Renewable Energy, Advanced Manufacturing Office and the Office of Electricity Delivery and Energy Reliability (OE) - Transformer Resilience and Advanced Components (TRAC) Program. 17 | 18 | ## License 19 | 20 | 3dThesis is distributed under an [open source 3-clause BSD license](LICENSE). 21 | 22 | ## Build 23 | 24 | 3dThesis requires a C++ compiler and OpenMP for on-node parallelism. 3dThesis will optionally use MPI if available on the system. 25 | 26 | 3dThesis primarily support CMake builds. A minimal example build and install looks like: 27 | 28 | ```bash 29 | cd ./3DThesis 30 | cmake \ 31 | -B build \ 32 | -D CMAKE_INSTALL_PREFIX=install 33 | cmake --build build 34 | cmake --install build 35 | ``` 36 | 37 | An additional example is shown in example_build.sh 38 | 39 | 3dThesis still supports `make`, but this support is planned to be removed. Options inside the `makefile` can be changed as needed for the local hardware. Run `make` from the commandline in order to build 3dThesis. 40 | 41 | ## Run 42 | 43 | By default, 3dThesis will be built within `./build/application`. The input files described below can be modified as needed and then 3dThesis can be run on the commandline: `./build/application/3dThesis` 44 | 45 | ## Inputs 46 | 47 | This section shows how to create a custom simulation with all tunable parameters. 48 | If a necessary keyword is not included, often the program will default to values found in the test case. Note when editing files, it is important to begin each group with a brace `{`, as well as close each group `}` as in the example files. 49 | 50 | When run from the command line, an input file is required (e.g. `./3DThesis ParamInput.txt`). If `Name` is set to `TestSim` in the input parameter file, then the data for the simulation can be found at `TestSim/Data`. All the files under `Simulation` are necessary, the rest are optional. Example input files for different simulation modes can be found in the `examples` directory. 51 | 52 | ### Simulation Files 53 | 54 | This set of files dictate everything having to do with the physics of the simulation, such as the material, the heat source, and the path of the heat source. All the inputs in this section are necessary. 55 | 56 | - Simulation Name 57 | - Mode file 58 | - Material file 59 | - Beam file 60 | - Path file 61 | 62 | #### Mode File 63 | 64 | This file contains all information about how the simulation is run. There are two types of modes: `Solidification` and `Snapshots`. Only one mode can be run at a time. 65 | 66 | Solidification 67 | 68 | - `Tracking` 69 | - `None` always calculates all points. 70 | - `Volume` always calculates all molten points. 71 | - `Surface` always calculates the surface of the molten pool. This tracking is fastest if only simulation solidification conditions. 72 | - `Timestep `: interval between when the temperatures are evaluated. Setting this value too low results in unnecessary computational cost; too high and solidification will be missed. 73 | - `OutputFrequency`: How many timesteps between subsequent outputs. The final result is always output so if intermediate results are not desired, set this value to be a large number. 74 | - `Secondary `: Calculates the secondary solidification characteristics. This is experimental and should never be used but has been left in for research purposes. 75 | 76 | Snapshots 77 | 78 | - `Times` Times at which to calculate the temperature snapshots (can either choose times OR scanFracs but not both). 79 | - `ScanFracs`: Times, in terms of percentage of the length of a scan path, at which to calculate the temperature snapshots. 80 | - `Tracking` 81 | - `None` always calculates all points. 82 | - `Volume` always calculates all molten points. 83 | - `Surface` always calculates the surface of the molten pool. For temperature snapshots, Volume and Surface behave identically. 84 | 85 | #### Material File 86 | 87 | This file contains all the material constants to be used by the simulation. The CET parameters are not necessary and are used to calculate the equiaxed grain fraction according to the model by Gaumann et al., Acta Materialia, 2001. 88 | 89 | - `T_0`: Initial/background temperature (K) 90 | - `T_L`: Liquidus temperature (K) 91 | - `k`: Thermal conductivity (W/(m*K)) 92 | - `c`: Specific Heat (J/(kg*K)) 93 | - `p`: Density (kg/m^3 ) 94 | - CET 95 | - `N0` 96 | - `n` 97 | - `a` 98 | 99 | #### Beam File 100 | 101 | This file contains information on the energy source, which is a volumetric gaussian. It should be noted that everything input here is equivalent to `√6 σ` as defined by the `D4σ` beam diameter. For example, if the standard deviation of an actual beam is `20 µm` and radially symmetric, both `Width_X` and `Width_Y` should be set to 48.9898e-6. 102 | 103 | Shape 104 | 105 | - `Width_X`: Width of the beam in the X direction (m) 106 | - `Width_Y`: Width of the beam in the Y direction (m) 107 | - `Depth_Z`: Penetration depth of the beam (m) 108 | 109 | Intensity 110 | 111 | - `Power`: Power of energy source (W) 112 | - `Efficiency`: Absorption efficiency of beam; refer to literature for accurate values. Typically it is 0.35 for laser powder bed fusion (PBF) and 0.85 for electron beam PBF. 113 | 114 | NOTE: To use multiple heat sources, use an asterisk (\*) to denote the wildcard (ex: “Beam.*.txt”). The program will start at “Beam.1.txt” then “Beam.2.txt” and keep going until “Beam.x.txt” does not exist. “Beam.x.txt” would be used with the path parameters found in “Path.x.txt” 115 | 116 | #### Path File 117 | 118 | This file dictate where and how the heat source travels. This file and all its components are necessary. An incomplete path file will cause failures. The format is different than other files and is in the form: 119 | `Mode X(mm) Y(mm) Z(mm) Pmod Vel(m/s)/Time(s)` 120 | 121 | The Mode dictates how the heat source moves. 122 | 123 | - `Mode` = 0 is a line melt. X, Y, Z controls where the beam travels TO and the last parameter controls the constant speed of this melt. 124 | - `Mode` = 1 is a spot melt. X, Y, Z control the location of the spot melt and the last parameter controls the duration of this melt. 125 | - `Pmod` is a power multiplier to the heat source. This is typically 0 or 1 to control the beam turning on and off. More advanced scan strategies have variable powers. In these cases, it’s generally best to let `Pmod` control the power and set the `Power` in the beam file to 1. 126 | 127 | Make sure that before a line melt, the starting point is correct! A raster pattern is best represented by alternating mode 1 and 0 where mode 1 is only there to set the start point and would have a Pmod of 0 and a short value for Time(s). 128 | 129 | NOTE: To use multiple heat sources, use an asterisk (\*) to denote the wildcard (ex: “Path.*.txt”). The program will start at “Path.1.txt” then “Path.2.txt” and keep going until “Path.x.txt” does not exist. “Path.x.txt” would be used with the beam parameters found in “Beam.x.txt” 130 | 131 | ### Option Files 132 | 133 | This set of files dictate everything having to do with the numerics of the simulation, such as the domain considered as well as various settings. These files are optional but it is highly recommended to understand how to change these for specific uses as they have a large impact on the speed and accuracy of the simulation. Default behavior is specified. 134 | 135 | #### Domain File 136 | 137 | This file contains information about the domain over which to calculate the temperature solution. It contains ways to control the resolution and bounds of the simulation domain but highly recommended to use. Boundary conditions can be set to provide somewhat nearly insulative boundaries using the method of images (just 1 iteration). For very thins walls (<1mm), this may not be enough to provide a completely insulative effect. Keep in mind that each point is calculated independently, so increasing the resolution by a factor of 2 in each direction will slow down the simulation by a factor of 8. 138 | 139 | - `X` 140 | - `Min`: Minimum X value 141 | - `Max`: Maximum X value 142 | - `Res`: Resolution in the X-direction of the domain (either Res OR Num can be used) 143 | - `Num`: Number of unique X-values in the grid. If this is set to 1, only the Max value is used. (either Res OR Num can be used) 144 | - `Y` 145 | - `Min` 146 | - `Max` 147 | - `Res` 148 | - `Num` 149 | - `Z` 150 | - `Min` 151 | - `Max`: Typically set to 0 (where the top surface is) 152 | - `Res` 153 | - `Num` 154 | - `BoundaryConditions` 155 | - `X_min` 156 | - `X_max` 157 | - `Y_min` 158 | - `Y_max` 159 | - `Custom` 160 | - `File`: File which has a specific set (x,y,z) coordinates to be used for the domain. Note: Using a point file negates any tracking modes (`Volume` and `Surface`), thus all points will always calculated. 161 | 162 | Note: Not specifying Min or Max value results in the unspecified value being calculed via the path file(s) with a domain file 500 µm buffer on each side and a 250 µm simulated depth. Not specifying a `Res` or `Num` results in a default resolution of 50 µm. 163 | 164 | #### Output File 165 | 166 | This file contains all variables which can be output. A value of 0 indicated to not output the variable whereas a value of 1 indicates that variable should be output. Most variables default to a value of 0. The memory required to run a simulation increases when more outputs are selected; therefore, it is good practice to output only the necessary or desired information. 167 | 168 | - Grid 169 | - `x`: x-coordinate 170 | - `y`: y-coordinate 171 | - `z`: z-coordinate 172 | - Temperature 173 | - `T`: Temperature 174 | - `T_hist`: Temperature history. Note: this will create a separate file for each point. DO NOT USE if many points are in the domain 175 | - Solidification 176 | - `tSol`: Solidification time 177 | - `G`: Magnitude of the thermal gradient at solidification 178 | - `Gx`: x-component of normalized gradient 179 | - `Gy`: y-component of normalized gradient 180 | - `Gz`: z-component of normalized gradient 181 | - `V`: Velocity of solidification front 182 | - `dTdt`: Cooling rate 183 | - `eqFrac`: Equiaxed fraction 184 | - `RDF`: Export results in “Reduced Data Format” compatible with ExaCA 185 | - `numMelt`: Number of times a point melted and solidified 186 | - `MP_Stats`: Output the maximum width and length of the melt pool at each point. MP_Stats may have unexpected behavior when using with MPI domain decomposition or multiple beams. 187 | - Solidification+ 188 | - `H`: Magnitude of the orthogonal differential change in the solidification gradient in the direction of the solidification gradient 189 | - `Hx`: x-component of normalized H 190 | - `Hy`: y-component of normalized H 191 | - `Hz`: z-component of normalized H 192 | 193 | #### Settings File 194 | 195 | This file contains all the tunable parameters for how the simulation is run. Generally, only `MaxThreads` (which speeds up the simulation if the hardware being used has at least that many available threads) needs to be changed. 196 | 197 | - Temperature 198 | - `Sol_Tol`: Controls how close the temperature must be to the liquidus temperature (as a fraction) before the search algorithm stops. As an example, if `T_L = 10K`, `timestep=0.1s`, `T(t=1.7s)=12K`, `T(t=1.8s)=9K`, and `Sol_Tol=1e-2` THEN the search algorithm won’t stop trying to find the EXACT time the point solidified until it finds a time where `9.9K < T < 10.1K`. Defaults to `1e-3` 199 | - `Sol_Iter`: Controls the maximum number of iterations for the algorithm used above. Defaults to 10. 200 | - `Cutoff_Peak`: Helps control how far back in time the integration is done. Defaults to 1e-9. For example, if a simulation runs for 30s, maybe the first 5s have so little temperature contribution that they aren’t worth integrating. This setting rarely gets used since the introduction of the path compression algorithm. 201 | - `Cutoff_T0TL`: Another setting to help control how far back in time the integration is done. Defaults to 1e-9. 202 | - Path 203 | - `Buffer`: Control how far outside the domain the scan path is considered (in meters). For example, if a scan file contains information for 6 cubes, but you only want to analyze one of them. Set the domain to include the 1 cube and set the buffer to 0 or 0.001 (1mm) or something small. This will result in a large speedup. 204 | - `Compression`: Binary toggle (0-off, 1-on) for using a path compression algorithm. This is primary useful for LARGE scan paths (1 Mb or larger) with smooth-ish movement of the heat source (ex: raster or point raster) rather than discontinuous movement (ex: random point fill). It does so by compressing multiple nearby diffuse heat sources into a single source. There is a small loss in accuracy but a large speedup for certain simulations. 205 | - Compute 206 | - `MaxThreads`: Number of threads to use for the simulation. 207 | 208 | ## Outputs 209 | 210 | Analysis of 3dThesis results can be done with a variety of methods (e.g. Python), but quick visualization is possible with [Paraview](https://www.paraview.org/). 211 | 212 | For an example visualization, first run the `examples/solidification` case and ensure that the run finished successfully. The data should be found in `examples/solidification/Data/examples/solidification/Data/solidification.Solidification.Final.0.csv`. To view this file in Paraview, open it through the folder icon, in the top left, and then click the apply button. A table of data should appear on the right side of the screen. To visualize this data in 3D, click on the file name on the Pipeline Browser (left side of the screen by default) to select the file, then apply the `Table to Points` filter under `Filters->Alphabetical`. 213 | 214 | Under the Properties tab (on the left side under the Pipeline Browser by default), three dropdown menus should appear titled “X Column”, “Y Column”, and “Z Column”. Simply change these to be `x`, `y`, and `z`, click apply, and exit out of the tabular view of the data. Now at the top of the screen, locate the dropdown menu that says, “Solid Color.” Change this to `G` (the thermal solidification gradient). The point size and color scale can be changed using other options in the left menu (under the “Coloring” and “Styling” groups respectively). 215 | -------------------------------------------------------------------------------- /src/Grid.h: -------------------------------------------------------------------------------- 1 | /**************************************************************************** 2 | * Copyright (c) 2019 UT-Battelle, LLC * 3 | * All rights reserved. * 4 | * * 5 | * This file is part of 3dThesis. 3dThesis is distributed under a * 6 | * BSD 3-clause license. For the licensing terms see the LICENSE file in * 7 | * the top-level directory. * 8 | * * 9 | * SPDX-License-Identifier: BSD-3-Clause * 10 | ****************************************************************************/ 11 | 12 | #pragma once 13 | #include "DataStructs.h" 14 | #include "Util.h" 15 | #include "Calc.h" 16 | 17 | #include 18 | #include 19 | 20 | #include 21 | #include 22 | #include 23 | #include 24 | #include 25 | 26 | using std::bind; 27 | using std::function; 28 | using std::vector; 29 | using std::string; 30 | using std::to_string; 31 | using std::placeholders::_1; 32 | 33 | 34 | ////// Modes (exclusive) 35 | //// Snapshot 36 | // Calculates temperature at all points at end of scan path 37 | // 38 | //// Temp History 39 | // Tracks temperature history for select points 40 | // 41 | //// Solidificiation 42 | // Calculates solidification conditions 43 | // 44 | //// Solidification+ 45 | // Calculates primary and secondary solidifiaction conditions 46 | // 47 | //// Reduced Data Format 48 | // Calculates melting and solidification events 49 | // 50 | 51 | ////// Heat Source 52 | //// Many Beams 53 | // Usual Gaussian (BeamX) 54 | // Single Path File Per Gaussian (PathX) 55 | 56 | class Grid { 57 | private: 58 | double xmin = -DBL_MAX; 59 | double xmax = DBL_MAX; 60 | double ymin = -DBL_MAX; 61 | double ymax = DBL_MAX; 62 | double zmin = -DBL_MAX; 63 | double zmax = DBL_MAX; 64 | 65 | uint16_t xnum = 0; 66 | uint16_t ynum = 0; 67 | uint16_t znum = 0; 68 | 69 | uint16_t* i = NULL; // index - always needed 70 | uint16_t* j = NULL; // index - always needed 71 | uint16_t* k = NULL; // index - always needed 72 | 73 | bool* T_calc_flag = NULL; // make sure temperature only calculated once - needed from any MP tracking mode 74 | bool* output_flag = NULL; // should a point be output - needed for MP tracking modes 75 | 76 | double* x = NULL; // coordinate - always needed 77 | double* y = NULL; // coordinate - always needed 78 | double* z = NULL; // coordinate - always needed 79 | 80 | double* T = NULL; // temperature - always needed 81 | double* T_last = NULL; // last temperature - needed for solidification times 82 | 83 | double* tSol = NULL; 84 | double* G = NULL; // solidfication gradient - store if output 85 | double* V = NULL; // solidfication velocity - store if output 86 | double* Gx = NULL; // solidification direction - store if output 87 | double* Gy = NULL; // solidification direction - store if output 88 | double* Gz = NULL; // solidification direction - store if output 89 | double* dTdt = NULL; // cooling rate - store if output 90 | double* eqFrac = NULL; // equiaxed fraction from CET - store if output 91 | uint16_t* numMelt = NULL; 92 | 93 | double* H = NULL; // secondary solidification magnitude - store if output 94 | double* Hx = NULL; // secondary solidification direction - store if output 95 | double* Hy = NULL; // secondary solidification direction - store if output 96 | double* Hz = NULL; // secondary solidification direction - store if output 97 | 98 | double* depth = NULL; // max depth under point - store if output 99 | 100 | vector* T_hist = NULL; // temperature history - store if output 101 | vector* t_hist = NULL; // iteration history - store if output 102 | 103 | vector* RDF_tm = NULL; // melting time for reduced data format - store if output 104 | vector* RDF_tl = NULL; // liquid time for reduced data format - store if output 105 | vector* RDF_cr = NULL; // cooling rate for reduced data format - store if output 106 | 107 | double* MP_Width = NULL; // maximum meltpool width - store if output 108 | double* MP_Length = NULL; // maximum meltpool length - store if output 109 | double* MP_Depth = NULL; // maximum meltpool depth - store if output 110 | 111 | vector RRDF_idxs; // indices for doubly reduced data format - initilaize size if used 112 | vector RRDF_ts; // times for doubly reduced data format - initilaize size if used 113 | vector RRDF_Ts; // temperatures for doubly reduced data format - initilaize size if used 114 | 115 | vector outputNames; // what all to output ("x", "y", "z", etc.) 116 | vector> outputFuncs; // the functions for outputting the variables 117 | 118 | public: 119 | Grid() {}; 120 | Grid(Simdat& sim) { 121 | 122 | xmin = sim.domain.xmin; xmax = sim.domain.xmax; xnum = sim.domain.xnum; 123 | ymin = sim.domain.ymin; ymax = sim.domain.ymax; ynum = sim.domain.ynum; 124 | zmin = sim.domain.zmin; zmax = sim.domain.zmax; znum = sim.domain.znum; 125 | 126 | const int pnum = sim.domain.pnum; 127 | 128 | i = new uint16_t[pnum](); 129 | j = new uint16_t[pnum](); 130 | k = new uint16_t[pnum](); 131 | 132 | if (sim.domain.customPoints) { 133 | x = new double[pnum](); 134 | y = new double[pnum](); 135 | z = new double[pnum](); 136 | } 137 | 138 | if (sim.output.x) { 139 | outputNames.push_back("x"); 140 | outputFuncs.push_back(bind(&Grid::get_x, this, _1)); 141 | } 142 | 143 | if (sim.output.y) { 144 | outputNames.push_back("y"); 145 | outputFuncs.push_back(bind(&Grid::get_y, this, _1)); 146 | } 147 | 148 | if (sim.output.z) { 149 | outputNames.push_back("z"); 150 | outputFuncs.push_back(bind(&Grid::get_z, this, _1)); 151 | } 152 | 153 | T = new double[pnum](); 154 | if (sim.output.T) { 155 | outputNames.push_back("T"); 156 | outputFuncs.push_back(bind(&Grid::get_T, this, _1)); 157 | } 158 | if (sim.output.T_hist) { 159 | T_hist = new vector[pnum]; 160 | t_hist = new vector[pnum]; 161 | } 162 | 163 | InitializeGridPoints(sim); 164 | 165 | if (sim.param.mode == "Solidification" || sim.param.tracking != "None") { 166 | T_calc_flag = new bool[pnum](); 167 | output_flag = new bool[pnum](); 168 | } 169 | 170 | if (sim.param.mode == "Stork"){ 171 | RRDF_idxs.reserve(pnum); 172 | RRDF_ts.reserve(2*pnum); 173 | RRDF_Ts.reserve(16*pnum); 174 | } 175 | 176 | if (sim.param.mode == "Solidification") { 177 | T_last = new double[pnum](); 178 | if (sim.output.tSol) { 179 | sim.util.do_sol = true; 180 | tSol = new double[pnum](); 181 | outputNames.push_back("tSol"); 182 | outputFuncs.push_back(bind(&Grid::get_tSol, this, _1)); 183 | } 184 | if (sim.output.G) { 185 | sim.util.do_sol = true; 186 | G = new double[pnum](); 187 | outputNames.push_back("G"); 188 | outputFuncs.push_back(bind(&Grid::get_G, this, _1)); 189 | } 190 | if (sim.output.V) { 191 | sim.util.do_sol = true; 192 | V = new double[pnum](); 193 | outputNames.push_back("V"); 194 | outputFuncs.push_back(bind(&Grid::get_V, this, _1)); 195 | } 196 | if (sim.output.Gx) { 197 | sim.util.do_sol = true; 198 | Gx = new double[pnum](); 199 | outputNames.push_back("Gx"); 200 | outputFuncs.push_back(bind(&Grid::get_Gx, this, _1)); 201 | } 202 | if (sim.output.Gy) { 203 | sim.util.do_sol = true; 204 | Gy = new double[pnum](); 205 | outputNames.push_back("Gy"); 206 | outputFuncs.push_back(bind(&Grid::get_Gy, this, _1)); 207 | } 208 | if (sim.output.Gz) { 209 | sim.util.do_sol = true; 210 | Gz = new double[pnum](); 211 | outputNames.push_back("Gz"); 212 | outputFuncs.push_back(bind(&Grid::get_Gz, this, _1)); 213 | } 214 | if (sim.output.dTdt) { 215 | sim.util.do_sol = true; 216 | dTdt = new double[pnum](); 217 | outputNames.push_back("dTdt"); 218 | outputFuncs.push_back(bind(&Grid::get_dTdt, this, _1)); 219 | } 220 | if (sim.output.eqFrac) { 221 | sim.util.do_sol = true; 222 | eqFrac = new double[pnum](); 223 | outputNames.push_back("eqFrac"); 224 | outputFuncs.push_back(bind(&Grid::get_eqFrac, this, _1)); 225 | } 226 | if (sim.output.depth && sim.param.tracking == "Surface") { 227 | depth = new double[pnum](); 228 | outputNames.push_back("depth"); 229 | outputFuncs.push_back(bind(&Grid::get_depth, this, _1)); 230 | } 231 | if (sim.output.numMelt) { 232 | if (!sim.output.RDF) { numMelt = new uint16_t[pnum](); } 233 | outputNames.push_back("numMelt"); 234 | outputFuncs.push_back(bind(&Grid::get_numMelt, this, _1)); 235 | } 236 | if (sim.output.RDF) { 237 | sim.util.do_sol = true; 238 | RDF_tm = new vector[pnum](); 239 | RDF_tl = new vector[pnum](); 240 | RDF_cr = new vector[pnum](); 241 | } 242 | if (sim.output.mp_stats){ 243 | MP_Width = new double[pnum](); 244 | MP_Length = new double[pnum](); 245 | MP_Depth = new double[pnum](); 246 | 247 | outputNames.push_back("MP_width"); 248 | outputNames.push_back("MP_length"); 249 | outputNames.push_back("MP_depth"); 250 | 251 | outputFuncs.push_back(bind(&Grid::get_mpWidth, this, _1)); 252 | outputFuncs.push_back(bind(&Grid::get_mpLength, this, _1)); 253 | outputFuncs.push_back(bind(&Grid::get_mpDepth, this, _1)); 254 | } 255 | } 256 | 257 | if (sim.param.mode == "Solidification" && sim.param.secondary == true) { 258 | if (sim.output.H) { 259 | H = new double[pnum]; 260 | outputNames.push_back("H"); 261 | outputFuncs.push_back(bind(&Grid::get_H, this, _1)); 262 | } 263 | if (sim.output.Hx) { 264 | Hx = new double[pnum]; 265 | outputNames.push_back("Hx"); 266 | outputFuncs.push_back(bind(&Grid::get_Hx, this, _1)); 267 | } 268 | if (sim.output.Hy) { 269 | Hy = new double[pnum]; 270 | outputNames.push_back("Hy"); 271 | outputFuncs.push_back(bind(&Grid::get_Hy, this, _1)); 272 | } 273 | if (sim.output.Hz) { 274 | Hz = new double[pnum]; 275 | outputNames.push_back("Hz"); 276 | outputFuncs.push_back(bind(&Grid::get_Hz, this, _1)); 277 | } 278 | } 279 | 280 | } 281 | ~Grid() {}; 282 | void InitializeGridPoints(const Simdat&); 283 | void Output(const Simdat&, const string); 284 | vector> Output_Table(const Simdat& sim); 285 | void Output_T_hist(const Simdat&, const string); 286 | void Output_RDF(const Simdat&, const string); 287 | void Output_RRDF_csv(const Simdat&, const string); 288 | void Output_RRDF_bin(const Simdat&, const string); 289 | 290 | double Calc_T(const double, const Nodes&, const Simdat&, const bool, const int); // Returns temperature 291 | void Solidify(const double, const Simdat&, const int); // Solidifies point 292 | double Calc_Solidification_time(const double, const Simdat&, const int); // Returns time of solidification 293 | vector> Calc_Solidficiaton_Primary(const double, const Nodes&, const int); // Returns {Gx,Gy,Gz,Laplace,dT_t} 294 | vector> Calc_Solidficiaton_Secondary(const double, const Nodes&, const int); 295 | void Set_Solidficiaton_Primary(const vector&, const Simdat&, const int); 296 | void Set_Solidficiaton_Secondary(const vector&, const vector&, const Simdat&, const int); 297 | 298 | // Gets // 299 | inline int get_i(const int p) { return i[p]; } 300 | inline int get_j(const int p) { return j[p]; } 301 | inline int get_k(const int p) { return k[p]; } 302 | 303 | bool get_T_calc_flag(const int p) { return T_calc_flag[p]; } 304 | bool get_output_flag(const int p) { 305 | bool out = true; 306 | if (output_flag != NULL){ out = output_flag[p]; } 307 | return out; 308 | } 309 | 310 | double get_x(const int p) { 311 | double xcoord = xmax; 312 | if (x != NULL) { xcoord = x[p]; } 313 | else if (xnum - 1) { xcoord = xmin + (double(i[p]) * ((xmax - xmin) / (xnum - 1))); } 314 | return xcoord; 315 | } 316 | double get_y(const int p) { 317 | double ycoord = ymax; 318 | if (y != NULL) { ycoord = y[p]; } 319 | else if (ynum - 1) { ycoord = ymin + (double(j[p]) * ((ymax - ymin) / (ynum - 1))); } 320 | return ycoord; 321 | } 322 | double get_z(const int p) { 323 | double zcoord = zmax; 324 | if (z != NULL) { zcoord = z[p]; } 325 | else if (znum - 1) { zcoord = zmin + (double(k[p]) * ((zmax - zmin) / (znum - 1))); } 326 | return zcoord; 327 | } 328 | 329 | double get_T(const int p) { return T[p]; } 330 | double get_T_last(const int p) { return T_last[p]; } 331 | 332 | double get_tSol(const int p) { return tSol[p]; } 333 | double get_G(const int p) { return G[p]; } 334 | double get_Gx(const int p) { return Gx[p]; } 335 | double get_Gy(const int p) { return Gy[p]; } 336 | double get_Gz(const int p) { return Gz[p]; } 337 | double get_V(const int p) { return V[p]; } 338 | double get_dTdt(const int p) { return dTdt[p]; } 339 | double get_eqFrac(const int p) { return eqFrac[p]; } 340 | double get_depth(const int p) { 341 | return (depth[p] * ((zmax - zmin) / (znum - 1))); 342 | } 343 | uint16_t get_numMelt(const int p) { 344 | if (numMelt != NULL) { 345 | return numMelt[p]; 346 | } 347 | else { 348 | return get_RDF_tm(p).size(); 349 | } 350 | } 351 | 352 | double get_H(const int p) { return G[p]; } 353 | double get_Hx(const int p) { return Hx[p]; } 354 | double get_Hy(const int p) { return Hy[p]; } 355 | double get_Hz(const int p) { return Hz[p]; } 356 | 357 | vector get_T_hist(const int p) { return T_hist[p]; } 358 | vector get_t_hist(const int p) { return t_hist[p]; } 359 | 360 | vector get_RDF_tm(const int p) { return RDF_tm[p]; } 361 | vector get_RDF_tl(const int p) { return RDF_tl[p]; } 362 | vector get_RDF_cr(const int p) { return RDF_cr[p]; } 363 | 364 | double get_mpLength(const int p) { return MP_Length[p]; } 365 | double get_mpWidth(const int p) { return MP_Width[p]; } 366 | double get_mpDepth(const int p) { return MP_Depth[p]; } 367 | 368 | vector& get_RRDF_idxs() { return RRDF_idxs; } 369 | vector& get_RRDF_ts() { return RRDF_ts; } 370 | vector& get_RRDF_Ts() { return RRDF_Ts; } 371 | 372 | // Sets // 373 | void set_T_calc_flag(const bool b, const int p) { if (T_calc_flag != NULL) { T_calc_flag[p] = b; } } 374 | void set_output_flag(const bool b, const int p) { if (output_flag != NULL) { output_flag[p] = b; } } 375 | 376 | void set_T(const double d, const int p) { if (T != NULL) { T[p] = d; } } 377 | void add_T_hist(const double d, const int p) { if (T_hist != NULL) { T_hist[p].push_back(d); } } 378 | void add_t_hist(const double d, const int p) { if (t_hist != NULL) { t_hist[p].push_back(d); } } 379 | 380 | void set_T_last(const int p) {if (T_last != NULL) { T_last[p] = T[p]; }} 381 | void set_T_last(const double d, const int p) { if (T_last != NULL) { T_last[p] = d; } } 382 | 383 | void set_tSol(const double d, const int p) { if (tSol != NULL) { tSol[p] = d; } } 384 | void set_G(const double d, const int p) { if (G != NULL) { G[p] = d; } } 385 | void set_Gx(const double d, const int p) { if (Gx != NULL) { Gx[p] = d; } } 386 | void set_Gy(const double d, const int p) { if (Gy != NULL) { Gy[p] = d; } } 387 | void set_Gz(const double d, const int p) { if (Gz != NULL) { Gz[p] = d; } } 388 | void set_V(const double d, const int p) { if (V != NULL) { V[p] = d; } } 389 | void set_dTdt(const double d, const int p) { if (dTdt != NULL) { dTdt[p] = d; } } 390 | void set_eqFrac(const double d, const int p) { if (eqFrac != NULL) { eqFrac[p] = d; } } 391 | void add_numMelt(const int p) { if (numMelt != NULL) { numMelt[p] += 1; } } 392 | 393 | void set_H(const double d, const int p) { if (H != NULL) { H[p] = d; } } 394 | void set_Hx(const double d, const int p) { if (Hx != NULL) { Hx[p] = d; } } 395 | void set_Hy(const double d, const int p) { if (Hy != NULL) { Hy[p] = d; } } 396 | void set_Hz(const double d, const int p) { if (Hz != NULL) { Hz[p] = d; } } 397 | 398 | void set_depth(const double d, const int p) { if (depth != NULL) { depth[p] = d; } } 399 | 400 | void add_RDF_tm(const double d, const int p) { 401 | if (RDF_tm != NULL) { 402 | if (RDF_tm[p].size() == RDF_tl[p].size()) {RDF_tm[p].push_back(d);} 403 | } 404 | } 405 | void add_RDF_tl(const double d, const int p) { 406 | if (RDF_tl != NULL) { RDF_tl[p].push_back(d); } 407 | } 408 | void add_RDF_cr(const double d, const int p) { 409 | if (RDF_cr != NULL) { RDF_cr[p].push_back(d); } 410 | } 411 | 412 | void set_mpLength(const double length, const int p) { MP_Length[p] = std::max(length, MP_Length[p]); } 413 | void set_mpWidth(const double width, const int p) { MP_Width[p] = std::max(width, MP_Width[p]); } 414 | void set_mpDepth(const double depth, const int p) { MP_Depth[p] = std::max(depth, MP_Depth[p]); } 415 | }; 416 | -------------------------------------------------------------------------------- /src/Grid.cpp: -------------------------------------------------------------------------------- 1 | /**************************************************************************** 2 | * Copyright (c) 2019 UT-Battelle, LLC * 3 | * All rights reserved. * 4 | * * 5 | * This file is part of 3dThesis. 3dThesis is distributed under a * 6 | * BSD 3-clause license. For the licensing terms see the LICENSE file in * 7 | * the top-level directory. * 8 | * * 9 | * SPDX-License-Identifier: BSD-3-Clause * 10 | ****************************************************************************/ 11 | 12 | #include "Grid.h" 13 | #include "Out.h" 14 | #include "Util.h" 15 | 16 | void Grid::InitializeGridPoints(const Simdat& sim) { 17 | if (sim.domain.customPoints) { 18 | #pragma omp for schedule(static) 19 | for (int p = 0; p < sim.domain.pnum; p++) { 20 | i[p] = p; 21 | j[p] = 0; 22 | k[p] = 0; 23 | 24 | x[p] = sim.domain.points[p].x; 25 | y[p] = sim.domain.points[p].y; 26 | z[p] = sim.domain.points[p].z; 27 | } 28 | } 29 | else { 30 | #pragma omp parallel num_threads(sim.settings.thnum) 31 | { 32 | double xp, yp, zp; 33 | int p; 34 | #pragma omp for schedule(static) 35 | for (int ii = 0; ii < sim.domain.xnum; ii++) { 36 | if (sim.domain.xnum == 1) { xp = sim.domain.xmax; } 37 | else { xp = sim.domain.xmin + ((double)ii * ((sim.domain.xmax - sim.domain.xmin) / ((double)sim.domain.xnum - 1))); } 38 | for (int jj = 0; jj < sim.domain.ynum; jj++) { 39 | if (sim.domain.ynum == 1) { yp = sim.domain.ymax; } 40 | else { yp = sim.domain.ymin + ((double)jj * ((sim.domain.ymax - sim.domain.ymin) / ((double)sim.domain.ynum - 1))); } 41 | for (int kk = 0; kk < sim.domain.znum; kk++) { 42 | if (sim.domain.znum == 1) { zp = sim.domain.zmax; } 43 | else { zp = sim.domain.zmin + ((double)kk * ((sim.domain.zmax - sim.domain.zmin) / ((double)sim.domain.znum - 1))); } 44 | p = Util::ijk_to_p(ii, jj, kk, sim); 45 | i[p] = ii; j[p] = jj; k[p] = kk; 46 | //x[p] = xp; y[p] = yp; z[p] = zp; 47 | } 48 | } 49 | } 50 | } 51 | } 52 | } 53 | 54 | vector> Grid::Output_Table(const Simdat& sim) { 55 | vector> data; 56 | vector row; 57 | // If, for some reason, nothing is being output: return 58 | const int numOut = outputNames.size(); 59 | if (numOut==0) { return data;} 60 | for (int p = 0; p < sim.domain.pnum; p++){ 61 | if (get_output_flag(p)){ 62 | for (int i = 0; i < numOut; i++){ 63 | row.push_back(outputFuncs[i](p)); 64 | } 65 | } 66 | data.push_back(row); 67 | row.clear(); 68 | } 69 | return data; 70 | } 71 | 72 | void Grid::Output(const Simdat& sim, const string name){ 73 | const int numOut = outputNames.size(); 74 | if (numOut==0) { return; } 75 | 76 | std::ofstream datafile; 77 | datafile.exceptions(std::ofstream::failbit | std::ofstream::badbit); 78 | string out_file = sim.files.dataDir + "/" + sim.files.name + "." + name + ".csv"; 79 | try { 80 | datafile.open(out_file.c_str()); 81 | 82 | datafile << outputNames[0]; 83 | for (int i = 1; i < numOut; i++) {datafile << "," << outputNames[i];} 84 | datafile << "\n"; 85 | 86 | for (int p = 0; p < sim.domain.pnum; p++) { 87 | if (get_output_flag(p)) { 88 | datafile << outputFuncs[0](p); 89 | for (int i = 1; i < numOut; i++) { datafile << "," << outputFuncs[i](p); } 90 | datafile << "\n"; 91 | } 92 | } 93 | 94 | } 95 | catch (const std::ofstream::failure& e) { std::cout << "Exception writing data file, check that Data directory exists\n"; } 96 | datafile.close(); 97 | return; 98 | 99 | } 100 | 101 | void Grid::Output_T_hist(const Simdat& sim, const string name) { 102 | // If T_hist isn't ouput: return 103 | if (T_hist == NULL) { return; } 104 | 105 | // If the points have the same times 106 | if (sim.param.tracking=="None"){ 107 | // Create datafile with list of points and their coordinates 108 | std::ofstream datafile; 109 | datafile.exceptions(std::ofstream::failbit | std::ofstream::badbit); 110 | string out_file = sim.files.dataDir + "/" + sim.files.name + name + ".csv"; 111 | try { 112 | datafile.open(out_file.c_str()); 113 | datafile << "x,y,z"; 114 | const int max_tnum = get_t_hist(0).size(); 115 | const int t_width = 1+static_cast(1+log10(max_tnum)); 116 | // for (int tnum=0;tnum& idxs = RRDF_idxs; 210 | const vector& ts = RRDF_ts; 211 | const vector& Ts = RRDF_Ts; 212 | 213 | // Get header info 214 | const uint32_t size = ts.size()/2; 215 | const uint32_t extent[3] = {static_cast(sim.domain.xnum), static_cast(sim.domain.ynum), static_cast(sim.domain.znum)}; 216 | const double doubles[5] = {static_cast(sim.domain.xmin), static_cast(sim.domain.ymin), static_cast(sim.domain.zmin), static_cast(sim.domain.xres), static_cast(sim.material.T_liq)}; 217 | 218 | // Output CSV 219 | std::ofstream datafile; 220 | datafile.exceptions(std::ofstream::failbit | std::ofstream::badbit); 221 | try { 222 | datafile.open(csvFile.c_str()); 223 | datafile << "x,y,z,t_prev,t_cur"; 224 | datafile << ",T000_prev,T001_prev,T010_prev,T011_prev,T100_prev,T101_prev,T110_prev,T111_prev"; 225 | datafile << ",T000_cur,T001_cur,T010_cur,T011_cur,T100_cur,T101_cur,T110_cur,T111_cur\n"; 226 | for (uint32_t p = 0; p < size; p++) { 227 | datafile << idxs[3*p+0] << "," << idxs[3*p+1] << "," << idxs[3*p+2] << "," << ts[2*p+0] << "," << ts[2*p+1]; 228 | for (int n=0;n<8;n++){ 229 | datafile << "," << Ts[16*p+n]; 230 | } 231 | for (int n=0;n<8;n++){ 232 | datafile << "," << Ts[16*p+8+n]; 233 | } 234 | datafile << "\n"; 235 | } 236 | } 237 | catch (const std::ofstream::failure& e) { std::cout << "Exception writing data file, check that Data directory exists\n"; } 238 | datafile.close(); 239 | } 240 | 241 | void Grid::Output_RRDF_bin(const Simdat& sim, const string name){ 242 | 243 | // Make file name 244 | const string binFile = sim.files.dataDir + "/" + sim.files.name + "." + name + ".bin"; 245 | 246 | // Set references to data 247 | const vector& idxs = RRDF_idxs; 248 | const vector& ts = RRDF_ts; 249 | const vector& Ts = RRDF_Ts; 250 | 251 | // Get header info 252 | const uint32_t size = ts.size()/2; 253 | const uint32_t extent[3] = {static_cast(sim.domain.xnum), static_cast(sim.domain.ynum), static_cast(sim.domain.znum)}; 254 | const double doubles[5] = {static_cast(sim.domain.xmin), static_cast(sim.domain.ymin), static_cast(sim.domain.zmin), static_cast(sim.domain.xres), static_cast(sim.material.T_liq)}; 255 | 256 | // Open up binary file 257 | std::ofstream os(binFile, std::ios::binary); 258 | 259 | // Write header to binary file 260 | os.write((const char*)&size, sizeof(uint32_t)); 261 | os.write((const char*)&extent, 3 * sizeof(uint32_t)); 262 | os.write((const char*)&doubles, 5 * sizeof(double)); 263 | 264 | // Write vectors to binary file 265 | os.write((const char*)&idxs[0], 3 * size * sizeof(uint32_t)); 266 | os.write((const char*)&ts[0], 2 * size * sizeof(double)); 267 | os.write((const char*)&Ts[0], 16 * size * sizeof(double)); 268 | 269 | // Close binary file 270 | os.close(); 271 | 272 | std::cout << "Sizes: " << size << "\t" << idxs.size() << "\t" << ts.size() << "\t" << Ts.size() << std::endl; 273 | } 274 | 275 | double Grid::Calc_T(const double t, const Nodes& nodes, const Simdat& sim, const bool set, const int p) { 276 | /************************************************************************************************** 277 | Heat transfer kernel for ellipsoidal Gaussian volumentric heat source 278 | Adapted from Nguyen et al., Welding Journal, 1999 (Eq. 7) 279 | **************************************************************************************************/ 280 | 281 | const double xp = get_x(p); 282 | const double yp = get_y(p); 283 | const double zp = get_z(p); 284 | 285 | double dT = 0; 286 | for (size_t iter = 0; iter < nodes.size; iter++) { 287 | 288 | const double dx = xp - nodes.xb[iter]; 289 | const double dy = yp - nodes.yb[iter]; 290 | const double dz = zp - nodes.zb[iter]; 291 | 292 | const double phi = exp(-3.0 * ((dx * dx * nodes.phix[iter]) + (dy * dy * nodes.phiy[iter]) + (dz * dz * nodes.phiz[iter])) + nodes.expmod[iter]); 293 | const double dT_seg = nodes.dtau[iter] * phi; 294 | 295 | dT += dT_seg; 296 | } 297 | 298 | const double T_temp = sim.material.T_init + dT; 299 | 300 | if (set) { 301 | set_T(T_temp, p); 302 | add_T_hist(T_temp, p); 303 | add_t_hist(t, p); 304 | set_T_calc_flag(true, p); 305 | if (T_temp >= sim.material.T_liq) { 306 | set_G(DBL_MIN, p); 307 | set_V(DBL_MIN, p); 308 | set_eqFrac(-1.0, p); 309 | set_output_flag(1, p); 310 | } 311 | } 312 | 313 | return T_temp; 314 | } 315 | 316 | void Grid::Solidify(const double t, const Simdat& sim, const int p) { 317 | add_numMelt(p); 318 | if (sim.util.do_sol == false) { return; } 319 | 320 | const double t_sol = Calc_Solidification_time(t, sim, p); 321 | set_tSol(t_sol, p); 322 | add_RDF_tl(t_sol, p); 323 | 324 | Nodes nodes; 325 | Calc::Integrate_Serial(nodes, sim, t_sol, true); 326 | 327 | if (!sim.param.secondary) { 328 | const vector> params = Calc_Solidficiaton_Primary(t_sol, nodes, p); 329 | const vector primaryParams = params[0]; 330 | Set_Solidficiaton_Primary(primaryParams, sim, p); 331 | } 332 | else { 333 | const vector> params = Calc_Solidficiaton_Secondary(t_sol, nodes, p); 334 | const vector primaryParams = params[0]; 335 | const vector secondaryParams = params[0]; 336 | Set_Solidficiaton_Secondary(primaryParams, secondaryParams, sim, p); 337 | } 338 | } 339 | 340 | double Grid::Calc_Solidification_time(const double t, const Simdat& sim, const int p) { 341 | 342 | // Controls iteration 343 | bool runFlag = true; 344 | int runIter = 0;; 345 | int maxIter = sim.settings.max_iter; 346 | 347 | // Temperatures 348 | double T2 = T[p]; 349 | double T1 = T_last[p]; 350 | double T0 = sim.material.T_liq; 351 | 352 | // Times 353 | double t2 = t; 354 | double t1 = t - sim.param.dt; 355 | double t0 = 0.0; 356 | 357 | double T_temp = 0.0; 358 | double T_err = 0.0; 359 | double err_limit = sim.settings.dttest; 360 | 361 | double m = 0.0; 362 | 363 | Nodes nodes; 364 | 365 | 366 | while (runFlag) { //Continue until you get a temperature really close to the solidification temp 367 | // Slope between two temperatures 368 | m = (T2 - T1) / (t2 - t1); 369 | 370 | // Solve for when the line intersects T0 371 | t0 = t1 + ((T0 - T1) / m); 372 | 373 | // Get Quadrature information at t0 374 | Util::ClearNodes(nodes); 375 | Calc::Integrate_Serial(nodes, sim, t0, 0); 376 | 377 | // Calculate Temperature at t0 378 | T_temp = Calc_T(t0, nodes, sim, 0, p); 379 | 380 | // How far is this temperature from T0? 381 | T_err = abs(1.0 - T_temp / T0); 382 | 383 | // Increment interation 384 | runIter++; 385 | 386 | // If error below error limit or iteration above iteration limit, stop iterating 387 | if ((T_err < err_limit) || (runIter >= maxIter)) { 388 | runFlag = false; 389 | } 390 | // Otherwise, use new point in root finding method 391 | else { 392 | if (T_temp > T0) { 393 | t1 = t0; 394 | T1 = T_temp; 395 | } 396 | else { 397 | t2 = t0; 398 | T2 = T_temp; 399 | } 400 | } 401 | } 402 | 403 | return t0; 404 | } 405 | 406 | vector> Grid::Calc_Solidficiaton_Primary(const double t, const Nodes& nodes, const int p) { 407 | /************************************************************************************************** 408 | Heat transfer kernel for ellipsoidal Gaussian volumentric heat source 409 | Adapted from Nguyen et al., Welding Journal, 1999 (Eq. 7) 410 | **************************************************************************************************/ 411 | 412 | const double xp = get_x(p); 413 | const double yp = get_y(p); 414 | const double zp = get_z(p); 415 | 416 | double dT = 0; 417 | double Gx_temp = 0; 418 | double Gy_temp = 0; 419 | double Gz_temp = 0; 420 | double Laplace = 0; 421 | double dT_t = 0; 422 | for (size_t iter = 0; iter < nodes.size; iter++) { 423 | 424 | const double dx = xp - nodes.xb[iter]; 425 | const double dy = yp - nodes.yb[iter]; 426 | const double dz = zp - nodes.zb[iter]; 427 | 428 | const double phi = exp(-3.0 * ((dx * dx * nodes.phix[iter]) + (dy * dy * nodes.phiy[iter]) + (dz * dz * nodes.phiz[iter])) + nodes.expmod[iter]); 429 | 430 | const double dT_seg = nodes.dtau[iter] * phi; 431 | 432 | dT += dT_seg; 433 | 434 | const double ddpx = (-6.0 * nodes.phix[iter]); 435 | const double ddpy = (-6.0 * nodes.phiy[iter]); 436 | const double ddpz = (-6.0* nodes.phiz[iter]); 437 | 438 | const double dpx = (ddpx * dx); 439 | const double dpy = (ddpy * dy); 440 | const double dpz = (ddpz * dz); 441 | 442 | Gx_temp += dT_seg * dpx; //x-gradient 443 | Gy_temp += dT_seg * dpy; //y-gradient 444 | Gz_temp += dT_seg * dpz; //z-gradient 445 | 446 | Laplace += dT_seg * (dpx * dpx + dpy * dpy + dpz * dpz + ddpx + ddpy + ddpz); //laplacian 447 | dT_t += (nodes.dtau[iter]== 0) ? phi : 0; //Notice that the interval of time we are integrating over is of size, that is because we are looking the instantaneous change at that time 448 | } 449 | 450 | vector primaryParams = { Gx_temp, Gy_temp, Gz_temp, Laplace, dT_t }; 451 | 452 | return { primaryParams }; 453 | } 454 | 455 | vector> Grid::Calc_Solidficiaton_Secondary(const double t, const Nodes& nodes, const int p) { 456 | /************************************************************************************************** 457 | Heat transfer kernel for ellipsoidal Gaussian volumentric heat source 458 | Adapted from Nguyen et al., Welding Journal, 1999 (Eq. 7) 459 | **************************************************************************************************/ 460 | 461 | const double xp = get_x(p); 462 | const double yp = get_y(p); 463 | const double zp = get_z(p); 464 | 465 | double dT = 0; 466 | double Gx_temp = 0; 467 | double Gy_temp = 0; 468 | double Gz_temp = 0; 469 | double Laplace = 0; 470 | double dT_t = 0; 471 | 472 | double dGxdx = 0; 473 | double dGxdy = 0; 474 | double dGxdz = 0; 475 | double dGydy = 0; 476 | double dGydz = 0; 477 | double dGzdz = 0; 478 | 479 | for (size_t iter = 0; iter < nodes.size; iter++) { 480 | 481 | const double dx = xp - nodes.xb[iter]; 482 | const double dy = yp - nodes.yb[iter]; 483 | const double dz = zp - nodes.zb[iter]; 484 | 485 | const double phi = exp(-3.0 * ((dx * dx * nodes.phix[iter]) + (dy * dy * nodes.phiy[iter]) + (dz * dz * nodes.phiz[iter])) + nodes.expmod[iter]); 486 | 487 | const double dT_seg = nodes.dtau[iter] * phi; 488 | 489 | dT += dT_seg; 490 | 491 | const double ddpx = (-6.0 * nodes.phix[iter]); 492 | const double ddpy = (-6.0 * nodes.phiy[iter]); 493 | const double ddpz = (-6.0* nodes.phiz[iter]); 494 | 495 | const double dpx = (ddpx * dx); 496 | const double dpy = (ddpy * dy); 497 | const double dpz = (ddpz * dz); 498 | 499 | Gx_temp += dT_seg * dpx; //x-gradient 500 | Gy_temp += dT_seg * dpy; //y-gradient 501 | Gz_temp += dT_seg * dpz; //z-gradient 502 | 503 | Laplace += dT_seg * (dpx * dpx + dpy * dpy + dpz * dpz + ddpx + ddpy + ddpz); //laplacian 504 | dT_t += (nodes.dtau[iter]== 0) ? phi : 0; //Notice that the interval of time we are integrating over is of size, that is because we are looking the instantaneous change at that time 505 | 506 | dGxdx += dT_seg * (dpx * dpx + ddpx); 507 | dGxdy += dT_seg * (dpx * dpy); 508 | dGxdz += dT_seg * (dpx * dpz); 509 | 510 | dGydy += dT_seg * (dpy * dpy + ddpy); 511 | dGydz += dT_seg * (dpy * dpz); 512 | 513 | dGzdz += dT_seg * (dpz * dpz + ddpz); 514 | } 515 | 516 | vector primaryParams = { Gx_temp, Gy_temp, Gz_temp, Laplace, dT_t }; 517 | vector secondaryParams = { dGxdx,dGxdy,dGxdz,dGydy,dGydz,dGzdz }; 518 | 519 | return { primaryParams,secondaryParams }; 520 | } 521 | 522 | void Grid::Set_Solidficiaton_Primary(const vector& primaryParams, const Simdat& sim, const int p) { 523 | const double Gx_temp = primaryParams[0]; 524 | const double Gy_temp = primaryParams[1]; 525 | const double Gz_temp = primaryParams[2]; 526 | const double Laplace = primaryParams[3]; 527 | const double dT_t = primaryParams[4]; 528 | 529 | const double G_temp = sqrt(Gx_temp * Gx_temp + Gy_temp * Gy_temp + Gz_temp * Gz_temp); 530 | 531 | const double Gxu_temp = Gx_temp / G_temp; 532 | const double Gyu_temp = Gy_temp / G_temp; 533 | const double Gzu_temp = Gz_temp / G_temp; 534 | 535 | const double dTdt_temp = abs(sim.material.a * Laplace + dT_t); 536 | const double V_temp = dTdt_temp / G_temp; 537 | 538 | const double eqFrac_temp = (1.0 - exp((-4 * PI * sim.material.cet_N0 / 3) * pow(G_temp * (sim.material.cet_n + 1) / (pow(sim.material.cet_a * V_temp, 1 / sim.material.cet_n)), -3))); 539 | 540 | set_G(G_temp, p); 541 | set_Gx(Gxu_temp, p); 542 | set_Gy(Gyu_temp, p); 543 | set_Gz(Gzu_temp, p); 544 | set_dTdt(dTdt_temp, p); 545 | set_V(V_temp, p); 546 | set_eqFrac(eqFrac_temp, p); 547 | add_RDF_cr(dTdt_temp, p); 548 | } 549 | 550 | void Grid::Set_Solidficiaton_Secondary(const vector& primaryParams, const vector& secondaryParams, const Simdat& sim, const int p) { 551 | const double Gx_temp = primaryParams[0]; 552 | const double Gy_temp = primaryParams[1]; 553 | const double Gz_temp = primaryParams[2]; 554 | const double Laplace = primaryParams[3]; 555 | const double dT_t = primaryParams[4]; 556 | 557 | const double dGxdx = secondaryParams[0]; 558 | const double dGxdy = secondaryParams[1]; 559 | const double dGxdz = secondaryParams[2]; 560 | const double dGydy = secondaryParams[3]; 561 | const double dGydz = secondaryParams[4]; 562 | const double dGzdz = secondaryParams[5]; 563 | 564 | const double G_temp = sqrt(Gx_temp * Gx_temp + Gy_temp * Gy_temp + Gz_temp * Gz_temp); 565 | 566 | const double Gxu_temp = Gx_temp / G_temp; 567 | const double Gyu_temp = Gy_temp / G_temp; 568 | const double Gzu_temp = Gz_temp / G_temp; 569 | 570 | const double dTdt_temp = abs(sim.material.a * Laplace + dT_t); 571 | const double V_temp = dTdt_temp / G_temp; 572 | 573 | const double eqFrac_temp = 1 - exp((-4 * PI * sim.material.cet_N0 / 3) * pow(G_temp * (sim.material.cet_n + 1) / (pow(sim.material.cet_a * V_temp, 1 / sim.material.cet_n)), -3)); 574 | 575 | set_G(G_temp, p); 576 | set_Gx(Gxu_temp, p); 577 | set_Gy(Gyu_temp, p); 578 | set_Gz(Gzu_temp, p); 579 | set_dTdt(dTdt_temp, p); 580 | set_V(V_temp, p); 581 | set_eqFrac(eqFrac_temp, p); 582 | add_RDF_cr(dTdt_temp, p); 583 | 584 | const double temp_x1 = dGxdx * Gxu_temp + dGydy * Gyu_temp + dGxdz * Gzu_temp; 585 | const double temp_y1 = dGxdy * Gxu_temp + dGydy * Gyu_temp + dGydz * Gzu_temp; 586 | const double temp_z1 = dGxdz * Gxu_temp + dGydz * Gyu_temp + dGzdz * Gzu_temp; 587 | 588 | const double temp_xyz1 = (temp_x1 * Gxu_temp + temp_y1 * Gyu_temp + temp_z1 * Gzu_temp); 589 | 590 | const double temp_x2 = Gxu_temp * temp_xyz1; 591 | const double temp_y2 = Gxu_temp * temp_xyz1; 592 | const double temp_z2 = Gxu_temp * temp_xyz1; 593 | 594 | const double Hx_temp = temp_x1 - temp_x2; 595 | const double Hy_temp = temp_y1 - temp_y2; 596 | const double Hz_temp = temp_z1 - temp_z2; 597 | 598 | const double H_temp = sqrt(Hx_temp * Hx_temp + Hy_temp * Hy_temp + Hz_temp * Hz_temp); 599 | const double Hxu_temp = Hx_temp / H_temp; 600 | const double Hyu_temp = Hy_temp / H_temp; 601 | const double Hzu_temp = Hz_temp / H_temp; 602 | 603 | set_H(H_temp, p); 604 | set_Hx(Hxu_temp, p); 605 | set_Hy(Hyu_temp, p); 606 | set_Hz(Hzu_temp, p); 607 | } 608 | -------------------------------------------------------------------------------- /src/Melt.cpp: -------------------------------------------------------------------------------- 1 | /**************************************************************************** 2 | * Copyright (c) 2019 UT-Battelle, LLC * 3 | * All rights reserved. * 4 | * * 5 | * This file is part of 3dThesis. 3dThesis is distributed under a * 6 | * BSD 3-clause license. For the licensing terms see the LICENSE file in * 7 | * the top-level directory. * 8 | * * 9 | * SPDX-License-Identifier: BSD-3-Clause * 10 | ****************************************************************************/ 11 | 12 | #include "Melt.h" 13 | 14 | #include 15 | #include 16 | #include 17 | 18 | using std::max; 19 | 20 | void beam_trace_perimeter(vector& test_pts, Grid& grid, const Simdat& sim, const double t_end_norm){ 21 | // For each path 22 | const int numPaths = sim.paths.size(); 23 | for (int pathNum=0;pathNum& path = sim.paths[pathNum]; 27 | const Beam& beam = sim.beams[pathNum]; 28 | 29 | // Out of bounds check 30 | const double t_sub = (sim.param.radiusCheck*sim.param.radiusCheck-1)*(beam.ax*beam.ax)/(12.0*sim.material.a); 31 | const double t_start = (t_end_norm-t_sub<0.0) ? 0.0 : t_end_norm-t_sub; 32 | const double t_end = t_end_norm; 33 | const double rCheck2 = sim.param.radiusCheck*sim.param.radiusCheck*beam.ax*beam.ax; 34 | 35 | // Set perimeter lambda 36 | auto add_perimeter_points = [&](int fixed_dim, bool is_x_fixed, int min_range, int max_range, double x, double y) { 37 | for (int var = min_range; var < max_range; ++var) 38 | { 39 | int i = is_x_fixed ? fixed_dim : var; 40 | int j = is_x_fixed ? var : fixed_dim; 41 | double x_perim = sim.domain.xmin + i * sim.domain.xres; 42 | double y_perim = sim.domain.ymin + j * sim.domain.yres; 43 | double dx = (x_perim - x); 44 | double dy = (y_perim - y); 45 | if (dx * dx + dy * dy < rCheck2) { 46 | int p = Util::ijk_to_p(i,j,sim.domain.znum-1,sim); 47 | // TODO::DEBUG 48 | //int p = (sim.domain.znum - 1) + sim.domain.znum * j + sim.domain.znum * sim.domain.ynum * i; 49 | if (!grid.get_T_calc_flag(p)) { 50 | test_pts.push_back(p); 51 | grid.set_T_calc_flag(true, p); 52 | } 53 | } 54 | } 55 | }; 56 | 57 | // Find segment associated with starting time 58 | int seg_start = 0; 59 | while (path[seg_start].seg_time < t_start && (seg_start + 1) != path.size()) { seg_start++; } 60 | 61 | // Find segment associated with ending time 62 | int seg_end = 0; 63 | while (path[seg_end].seg_time < t_end && (seg_end + 1) != path.size()) { seg_end++; } 64 | 65 | // For all path segments between starting and ending segments 66 | for (int seg = max(seg_start - 1, 0); seg <= seg_end; seg++) { 67 | 68 | // Get grid positions 69 | int x_grid_num = static_cast(std::floor((path[seg].sx - sim.domain.xmin) / sim.domain.xres)); 70 | int y_grid_num = static_cast(std::floor((path[seg].sy - sim.domain.ymin) / sim.domain.yres)); 71 | const int z_grid_num = sim.domain.znum-1; 72 | 73 | // Check conditions and call the lambda 74 | if (x_grid_num < 0) { add_perimeter_points(0, true, 0, sim.domain.ynum, path[seg].sx, path[seg].sy);} 75 | if (x_grid_num > sim.domain.xnum - 1) {add_perimeter_points(sim.domain.xnum - 1, true, 0, sim.domain.ynum, path[seg].sx, path[seg].sy);} 76 | if (y_grid_num < 0) {add_perimeter_points(0, false, 0, sim.domain.xnum, path[seg].sx, path[seg].sy);} 77 | if (y_grid_num > sim.domain.ynum - 1) {add_perimeter_points(sim.domain.ynum - 1, false, 0, sim.domain.xnum, path[seg].sx, path[seg].sy);} 78 | } 79 | 80 | // If the "current" segment is a line, also add the current point 81 | if (path[seg_end].smode == 0 && t_end(std::floor((current_beam.xb - sim.domain.xmin) / sim.domain.xres)); 86 | int y_grid_num = static_cast(std::floor((current_beam.yb - sim.domain.ymin) / sim.domain.yres)); 87 | const int z_grid_num = sim.domain.znum-1; 88 | 89 | // Check conditions and call the lambda 90 | if (x_grid_num < 0) { add_perimeter_points(0, true, 0, sim.domain.ynum, current_beam.xb, current_beam.yb);} 91 | if (x_grid_num > sim.domain.xnum - 1) {add_perimeter_points(sim.domain.xnum - 1, true, 0, sim.domain.ynum, current_beam.xb, current_beam.yb);} 92 | if (y_grid_num < 0) {add_perimeter_points(0, false, 0, sim.domain.xnum, current_beam.xb, current_beam.yb);} 93 | if (y_grid_num > sim.domain.ynum - 1) {add_perimeter_points(sim.domain.ynum - 1, false, 0, sim.domain.xnum, current_beam.xb, current_beam.yb);} 94 | } 95 | } 96 | } 97 | 98 | void Melt::beam_trace(vector& test_pts, Grid& grid, const Simdat& sim, const double t_start, const double t_end) { 99 | 100 | // Beam trace for perimeter 101 | beam_trace_perimeter(test_pts, grid, sim, t_end); 102 | 103 | // For each path 104 | const int numPaths = sim.paths.size(); 105 | for (int pathNum=0;pathNum& path = sim.paths[pathNum]; 109 | const Beam& beam = sim.beams[pathNum]; 110 | 111 | // Find segment associated with starting time 112 | int seg_start = 0; 113 | while (path[seg_start].seg_time < t_start && (seg_start + 1) != path.size()) { seg_start++; } 114 | 115 | // Find segment associated with ending time 116 | int seg_end = 0; 117 | while (path[seg_end].seg_time < t_end && (seg_end + 1) != path.size()) { seg_end++; } 118 | 119 | // For all path segments between starting and ending segments 120 | for (int seg = max(seg_start - 1, 0); seg <= seg_end; seg++) { 121 | 122 | // Get grid positions 123 | int x_grid_num = static_cast(std::floor((path[seg].sx - sim.domain.xmin) / sim.domain.xres)); 124 | int y_grid_num = static_cast(std::floor((path[seg].sy - sim.domain.ymin) / sim.domain.yres)); 125 | const int z_grid_num = sim.domain.znum-1; 126 | 127 | // If out of bounds, will be covered by perimeter tracker 128 | if (x_grid_num<0 || x_grid_num>sim.domain.xnum-1 || y_grid_num<0 || y_grid_num>sim.domain.ynum-1){ continue;} 129 | 130 | // Vector of test points 131 | for (int dx=0;dx<=1;dx++){ 132 | for (int dy=0;dy<=1;dy++){ 133 | const int x_grid = (x_grid_num + dx); 134 | const int y_grid = (y_grid_num + dy); 135 | const bool i_ob = (x_grid<0 || x_grid>sim.domain.xnum-1); 136 | const bool j_ob = (y_grid<0 || y_grid>sim.domain.ynum-1); 137 | if (i_ob || j_ob) { continue;} 138 | const int p = Util::ijk_to_p(x_grid, y_grid, z_grid_num, sim); 139 | 140 | if (!grid.get_T_calc_flag(p)) 141 | { 142 | test_pts.push_back(p); 143 | grid.set_T_calc_flag(true, p); 144 | } 145 | } 146 | } 147 | 148 | } 149 | 150 | // If the "current" segment is a line, also add the current point 151 | if (path[seg_end].smode == 0 && t_end(std::floor((current_beam.xb - sim.domain.xmin) / sim.domain.xres)); 156 | int y_grid_num = static_cast(std::floor((current_beam.yb - sim.domain.ymin) / sim.domain.yres)); 157 | const int z_grid_num = sim.domain.znum-1; 158 | 159 | // If out of bounds, will be covered by perimeter tracker 160 | if (x_grid_num<0 || x_grid_num>sim.domain.xnum-1 || y_grid_num<0 || y_grid_num>sim.domain.ynum-1){ continue;} 161 | 162 | // Vector of test points 163 | for (int dx=0;dx<=1;dx++){ 164 | for (int dy=0;dy<=1;dy++){ 165 | const int x_grid = (x_grid_num + dx); 166 | const int y_grid = (y_grid_num + dy); 167 | const bool i_ob = (x_grid<0 || x_grid>sim.domain.xnum-1); 168 | const bool j_ob = (y_grid<0 || y_grid>sim.domain.ynum-1); 169 | if (i_ob || j_ob) { continue;} 170 | const int p = Util::ijk_to_p(x_grid, y_grid, z_grid_num, sim); 171 | 172 | if (!grid.get_T_calc_flag(p)) 173 | { 174 | test_pts.push_back(p); 175 | grid.set_T_calc_flag(true, p); 176 | } 177 | } 178 | } 179 | } 180 | 181 | } 182 | return; 183 | } 184 | 185 | void Melt::neighbor_check(vector& test_pts, vector& liq_pts, vector& reset_pts, Grid& grid, vector& lock, const Nodes& nodes, const Simdat& sim, const double t, const bool surface_only) { 186 | vector test_tmp; 187 | //identify neighbors of liquid points ONLY ON SURFACE 188 | #pragma omp parallel num_threads(sim.settings.thnum) 189 | { 190 | vector th_liq_pts; 191 | vector th_test_tmp; 192 | vector th_reset_pts; 193 | //Find neighbors in test_pts for checking 194 | #pragma omp for schedule(dynamic,1+test_pts.size()/sim.settings.thnum/64) 195 | for (int it = 0; it < test_pts.size(); it++) { 196 | //Get i, j, k location of current point, then construct array of neighbors 197 | const int p = test_pts[it]; 198 | const int i = grid.get_i(p); 199 | const int j = grid.get_j(p); 200 | const int k = grid.get_k(p); 201 | 202 | if (surface_only){ 203 | for (int di=-1;di<=1;di++){ 204 | for (int dj=-1;dj<=1;dj++){ 205 | const int ni = i + di; 206 | const int nj = j + dj; 207 | if (ni<0 || ni>(sim.domain.xnum-1) || nj<0 || nj>(sim.domain.ynum-1)){ 208 | continue; 209 | } 210 | const int p_temp = Util::ijk_to_p(ni, nj, k, sim); 211 | 212 | bool Tflag = false; 213 | omp_set_lock(&(lock[p_temp])); 214 | if (!grid.get_T_calc_flag(p_temp)) { 215 | grid.set_T_calc_flag(true, p_temp); 216 | Tflag = true; 217 | } 218 | omp_unset_lock(&(lock[p_temp])); 219 | if (Tflag) { 220 | if (grid.Calc_T(t, nodes, sim, true, p_temp) >= sim.material.T_liq) { 221 | th_liq_pts.push_back(p_temp); 222 | th_test_tmp.push_back(p_temp); 223 | } 224 | th_reset_pts.push_back(p_temp); 225 | } 226 | } 227 | } 228 | } 229 | else{ 230 | for (int di=-1;di<=1;di++){ 231 | for (int dj=-1;dj<=1;dj++){ 232 | for (int dk=-1;dk<1;dk++){ 233 | const int ni = i + di; 234 | const int nj = j + dj; 235 | const int nk = k + dk; 236 | if (ni<0 || ni>(sim.domain.xnum-1) || nj<0 || nj>(sim.domain.ynum-1) || nk<0){ 237 | continue; 238 | } 239 | const int p_temp = Util::ijk_to_p(ni, nj, nk, sim); 240 | 241 | bool Tflag = false; 242 | omp_set_lock(&(lock[p_temp])); 243 | if (!grid.get_T_calc_flag(p_temp)) { 244 | grid.set_T_calc_flag(true, p_temp); 245 | Tflag = true; 246 | } 247 | omp_unset_lock(&(lock[p_temp])); 248 | if (Tflag) { 249 | if (grid.Calc_T(t, nodes, sim, true, p_temp) >= sim.material.T_liq) { 250 | th_liq_pts.push_back(p_temp); 251 | th_test_tmp.push_back(p_temp); 252 | } 253 | th_reset_pts.push_back(p_temp); 254 | } 255 | } 256 | } 257 | } 258 | } 259 | } 260 | #pragma omp critical 261 | { 262 | liq_pts.insert(liq_pts.end(), th_liq_pts.begin(), th_liq_pts.end()); 263 | test_tmp.insert(test_tmp.end(), th_test_tmp.begin(), th_test_tmp.end()); 264 | reset_pts.insert(reset_pts.end(), th_reset_pts.begin(), th_reset_pts.end()); 265 | } 266 | } 267 | test_pts.clear(); 268 | test_pts = test_tmp; 269 | } 270 | 271 | void Melt::calc_depth(vector& depths, vector& liq_pts, vector& reset_pts, Grid& grid, const Nodes& nodes, const Nodes& isegv_last, const Simdat& sim, const double t) { 272 | if (sim.domain.znum == 1) {return;} 273 | #pragma omp parallel num_threads(sim.settings.thnum) 274 | { 275 | vector th_reset_pts; 276 | #pragma omp for schedule(dynamic) 277 | for (int it = 0; it < liq_pts.size(); it++) { 278 | const int p = liq_pts[it]; 279 | const int i = grid.get_i(p); 280 | const int j = grid.get_j(p); 281 | int dnum = i * sim.domain.ynum + j; 282 | int depth = depths[dnum]; 283 | int flag = 1; 284 | while (true) { 285 | depth++; 286 | if (depth == sim.domain.znum) { break; } 287 | const int p_temp = Util::ijk_to_p(i, j, sim.domain.znum - 1 - depth, sim); 288 | th_reset_pts.push_back(p_temp); 289 | if (!(grid.Calc_T(t, nodes, sim, true, p_temp) >= sim.material.T_liq)) {break;} 290 | else { flag = 0; } 291 | } 292 | while (true) { 293 | depth--; 294 | if (depth == 0 || flag == 0) { break; } 295 | const int p_temp = Util::ijk_to_p(i, j, sim.domain.znum - 1 - depth, sim); 296 | th_reset_pts.push_back(p_temp); 297 | if (!(grid.Calc_T(t, nodes, sim, true, p_temp) >= sim.material.T_liq)) { 298 | if (depth != depths[dnum]) { 299 | const double T_last = grid.Calc_T(t - sim.param.dt, isegv_last, sim, false, p_temp); 300 | grid.set_T_last(T_last, p_temp); 301 | } 302 | grid.Solidify(t, sim, p_temp); 303 | } 304 | else { 305 | break; 306 | } 307 | } 308 | if (depth == sim.domain.znum) { depth--; } 309 | depths[dnum] = depth; 310 | } 311 | #pragma omp critical 312 | { 313 | reset_pts.insert(reset_pts.end(), th_reset_pts.begin(), th_reset_pts.end()); 314 | } 315 | } 316 | return; 317 | } 318 | 319 | void Melt::calc_depth_max(vector& depths, vector& depth_max, vector& liq_pts, Grid& grid, const Nodes& nodes, const Simdat& sim) { 320 | if (sim.domain.znum == 1 || sim.output.depth == 0) { return; } 321 | #pragma omp parallel for num_threads(sim.settings.thnum) schedule(static) 322 | for (int it = 0; it < liq_pts.size(); it++) { 323 | const int p = liq_pts[it]; 324 | const int i = grid.get_i(p); 325 | const int j = grid.get_j(p); 326 | int dnum = i * sim.domain.ynum + j; 327 | int depth_liq = depths[dnum]; 328 | 329 | double depth; 330 | if (depth_liq == sim.domain.znum - 1) { 331 | depth = depth_liq; 332 | } 333 | else { 334 | int depth_sol = depth_liq + 1; 335 | 336 | const int p_liq = Util::ijk_to_p(i, j, sim.domain.znum - 1 - depth_liq, sim); 337 | const int p_sol = Util::ijk_to_p(i, j, sim.domain.znum - 1 - depth_sol, sim); 338 | const double T_liq = grid.get_T(p_liq); 339 | const double T_sol = grid.get_T(p_sol); 340 | 341 | depth = depth_liq + (sim.material.T_liq - T_liq) / (T_sol - T_liq); 342 | } 343 | 344 | // If depth is greater 345 | if (depth > depth_max[dnum]) { 346 | // Store maximum depth 347 | depth_max[dnum] = depth; 348 | // Set that points and all points above it to have that max depth 349 | for (int d = depth_liq; d >= 0; d--) { 350 | const int p_temp = Util::ijk_to_p(i, j, sim.domain.znum - 1 - d, sim); 351 | grid.set_depth(depth, p_temp); 352 | } 353 | } 354 | 355 | } 356 | return; 357 | } 358 | 359 | void Melt::calc_mp_info(const vector& depths, Grid& grid, const Simdat& sim, const double t){ 360 | // If not outputting, don't do 361 | if (sim.output.mp_stats == 0) { return;} 362 | 363 | // This enables each segment to have its own local pools 364 | static list> multi_local_liq_pools; 365 | static list multi_segs; 366 | static list> multi_trigs; 367 | 368 | // This enables the starting search path segment to be quickly initialized each time. 369 | static int seg(0); 370 | 371 | // Set reference just to first path 372 | const vector& path = sim.paths[0]; 373 | 374 | // Keep incrementing up if t is greater than the end of the path segment but also below the end time of the scan 375 | while ((t > path[seg].seg_time) && (seg + 1 < path.size())) { 376 | // Increment segment 377 | seg++; 378 | // Add test points (if power is on) 379 | if (path[seg].sqmod>0){ 380 | // What segment information should be used? 381 | int seg_pt; 382 | if (path[seg].smode==0){seg_pt=seg-1;} 383 | else{seg_pt=seg;} 384 | 385 | // Get grid positions 386 | int x_grid_num = static_cast(std::floor((path[seg_pt].sx - sim.domain.xmin) / sim.domain.xres)); 387 | int y_grid_num = static_cast(std::floor((path[seg_pt].sy - sim.domain.ymin) / sim.domain.yres)); 388 | const int z_grid_num = sim.domain.znum-1; 389 | 390 | // Bring close to grid 391 | if (x_grid_num < 0){x_grid_num=-1;} 392 | if (x_grid_num > sim.domain.xnum - 1){ x_grid_num = sim.domain.xnum - 1;} 393 | 394 | // Bring close to grid 395 | if (y_grid_num < 0){y_grid_num=-1;} 396 | if (y_grid_num > sim.domain.xnum - 1){ y_grid_num = sim.domain.ynum - 1;} 397 | 398 | // Vector of test points 399 | vector test_pts; 400 | for (int dx=0;dx<=1;dx++){ 401 | for (int dy=0;dy<=1;dy++){ 402 | const int x_grid = (x_grid_num + dx); 403 | const int y_grid = (y_grid_num + dy); 404 | const bool i_ob = (x_grid<0 || x_grid>sim.domain.xnum-1); 405 | const bool j_ob = (y_grid<0 || y_grid>sim.domain.ynum-1); 406 | if (i_ob || j_ob) { continue;} 407 | const int p = (z_grid_num) + sim.domain.znum * y_grid + sim.domain.znum * sim.domain.ynum * x_grid; 408 | test_pts.push_back(p); 409 | } 410 | } 411 | 412 | // Add to final stuff 413 | multi_local_liq_pools.push_back(test_pts); 414 | multi_segs.push_back(seg); 415 | 416 | // Find angle and update time and angle vectors 417 | vector trigs(2); 418 | double angle; 419 | const double x_0 = path[seg-1].sx; 420 | const double y_0 = path[seg-1].sy; 421 | const double x_1 = path[seg].sx; 422 | const double y_1 = path[seg].sy; 423 | const double dx = x_1 - x_0; 424 | const double dy = y_1 - y_0; 425 | if (path[seg].smode == 1){angle = 0.0;} 426 | else{angle = atan2(dy, dx);} 427 | trigs[0] = sin(angle); 428 | trigs[1] = cos(angle); 429 | multi_trigs.push_back(trigs); 430 | } 431 | } 432 | 433 | // If we are on the same seg (and line melt), add current position too 434 | if (multi_segs.size()){ 435 | if (multi_segs.back()==seg && path[seg].smode==0){ 436 | vector local_test_pts = multi_local_liq_pools.back(); 437 | int_seg current_beam = Util::GetBeamLoc(t, seg, path, sim); 438 | 439 | // Get grid positions 440 | int x_grid_num = static_cast(std::floor((current_beam.xb - sim.domain.xmin) / sim.domain.xres)); 441 | int y_grid_num = static_cast(std::floor((current_beam.yb - sim.domain.ymin) / sim.domain.yres)); 442 | const int z_grid_num = sim.domain.znum-1; 443 | 444 | // Bring close to grid 445 | if (x_grid_num < 0){x_grid_num=-1;} 446 | if (x_grid_num > sim.domain.xnum - 1){ x_grid_num = sim.domain.xnum - 1;} 447 | 448 | // Bring close to grid 449 | if (y_grid_num < 0){y_grid_num=-1;} 450 | if (y_grid_num > sim.domain.xnum - 1){ y_grid_num = sim.domain.ynum - 1;} 451 | 452 | // Vector of test points 453 | for (int dx=0;dx<=1;dx++){ 454 | for (int dy=0;dy<=1;dy++){ 455 | const int x_grid = (x_grid_num + dx); 456 | const int y_grid = (y_grid_num + dy); 457 | const bool i_ob = (x_grid<0 || x_grid>sim.domain.xnum-1); 458 | const bool j_ob = (y_grid<0 || y_grid>sim.domain.ynum-1); 459 | if (i_ob || j_ob) { continue;} 460 | const int p = (z_grid_num) + sim.domain.znum * y_grid + sim.domain.znum * sim.domain.ynum * x_grid; 461 | local_test_pts.push_back(p); 462 | } 463 | } 464 | 465 | multi_local_liq_pools.back() = local_test_pts; 466 | } 467 | } 468 | else{ 469 | return; 470 | } 471 | 472 | // For all remaining segments 473 | auto seg_it = multi_segs.rbegin(); 474 | auto liq_it = multi_local_liq_pools.rbegin(); 475 | auto trig_it = multi_trigs.rbegin(); 476 | while (seg_it != multi_segs.rend()){ 477 | 478 | // Get local values (front to back) 479 | const int& local_seg = *seg_it; 480 | vector& local_test_pts = *liq_it; 481 | const vector& local_trigs = *trig_it; 482 | const double& sin_angle = local_trigs[0]; 483 | const double& cos_angle = local_trigs[1]; 484 | 485 | // Iterative loop from current point to find local, liquid points 486 | vector local_liq_pts; 487 | while (!local_test_pts.empty()){ 488 | local_neighbor_check(local_test_pts, local_liq_pts, grid, sim); 489 | } 490 | 491 | // If no liquid points 492 | if (local_liq_pts.empty()){ 493 | // Erase data using base iterators 494 | seg_it = list::reverse_iterator(multi_segs.erase((++seg_it).base())); 495 | liq_it = list>::reverse_iterator(multi_local_liq_pools.erase((++liq_it).base())); 496 | trig_it = list>::reverse_iterator(multi_trigs.erase((++trig_it).base())); 497 | } 498 | else{ 499 | // Set test points next iteration to current pool 500 | local_test_pts = local_liq_pts; 501 | 502 | // Calculate rotated x,y of liquid points 503 | double minRotX = std::numeric_limits::max(); 504 | double maxRotX= std::numeric_limits::lowest(); 505 | double minRotY = std::numeric_limits::max(); 506 | double maxRotY = std::numeric_limits::lowest(); 507 | for (const int& liq_pt:local_liq_pts){ 508 | const double x = grid.get_x(liq_pt); 509 | const double y = grid.get_y(liq_pt); 510 | 511 | const double x_rot = x*cos_angle + y*sin_angle; 512 | minRotX = std::min(x_rot, minRotX); 513 | maxRotX = std::max(x_rot, maxRotX); 514 | 515 | const double y_rot = -x*sin_angle + y*cos_angle; 516 | minRotY = std::min(y_rot, minRotY); 517 | maxRotY = std::max(y_rot, maxRotY); 518 | } 519 | 520 | // Calculate width and depth 521 | const double width = maxRotY - minRotY; 522 | const double length = maxRotX - minRotX; 523 | 524 | // Get depth 525 | const double depth = sim.domain.xres * (*std::max_element(depths.begin(), depths.end())); 526 | 527 | // Now add to all relevant points 528 | for (const int& liq_pt:local_liq_pts){ 529 | // Get i,j and 530 | const int i = grid.get_i(liq_pt); 531 | const int j = grid.get_j(liq_pt); 532 | int dnum = i * sim.domain.ynum + j; 533 | // For points in depth 534 | for (int d=0;d<=depths[dnum];d++){ 535 | const int p_temp = Util::ijk_to_p(i, j, sim.domain.znum - 1 - d, sim); 536 | grid.set_mpWidth(width, p_temp); 537 | grid.set_mpLength(length, p_temp); 538 | grid.set_mpDepth(depth, p_temp); 539 | } 540 | } 541 | 542 | // Update iterators 543 | ++seg_it; 544 | ++liq_it; 545 | ++trig_it; 546 | } 547 | } 548 | } 549 | 550 | void Melt::local_neighbor_check(vector& test_pts, vector& liq_pts, Grid& grid, const Simdat& sim) { 551 | 552 | vector test_tmp; 553 | 554 | //Find neighbors in test_pts for checking 555 | for (int it = 0; it < test_pts.size(); it++) { 556 | //Get i, j, k location of current point, then construct array of neighbors 557 | const int p = test_pts[it]; 558 | const int i = grid.get_i(p); 559 | const int j = grid.get_j(p); 560 | const int k = grid.get_k(p); 561 | for (int di=-1;di<=1;di++){ 562 | for (int dj=-1;dj<=1;dj++){ 563 | const int ni = i + di; 564 | const int nj = j + dj; 565 | if (ni<0 || ni>(sim.domain.xnum-1) || nj<0 || nj>(sim.domain.ynum-1)){ 566 | continue; 567 | } 568 | const int p_temp = Util::ijk_to_p(ni, nj, k, sim); 569 | if (grid.get_T_calc_flag(p_temp) && grid.get_T(p_temp)>=sim.material.T_liq) { 570 | grid.set_T_calc_flag(false, p_temp); 571 | test_tmp.push_back(p_temp); 572 | liq_pts.push_back(p_temp); 573 | } 574 | } 575 | } 576 | } 577 | 578 | test_pts.clear(); 579 | test_pts = test_tmp; 580 | } 581 | -------------------------------------------------------------------------------- /src/Calc.cpp: -------------------------------------------------------------------------------- 1 | /**************************************************************************** 2 | * Copyright (c) 2019 UT-Battelle, LLC * 3 | * All rights reserved. * 4 | * * 5 | * This file is part of 3dThesis. 3dThesis is distributed under a * 6 | * BSD 3-clause license. For the licensing terms see the LICENSE file in * 7 | * the top-level directory. * 8 | * * 9 | * SPDX-License-Identifier: BSD-3-Clause * 10 | ****************************************************************************/ 11 | 12 | #include 13 | #include 14 | #include "DataStructs.h" 15 | #include "Calc.h" 16 | #include "Util.h" 17 | 18 | #include 19 | #include 20 | 21 | void Calc::Integrate_Parallel(Nodes& nodes, const Simdat& sim, const double t, const bool isSol) { 22 | const int numThreads = sim.settings.thnum; 23 | static vector nodes_par; 24 | // If integrating in parallel 25 | if (numThreads>1) { 26 | // if the size is zero, integrate in parallel. Otherise, just take the last one (next time) from the previously calculated vector 27 | if (!nodes_par.size()) { 28 | nodes_par.resize(numThreads); 29 | #pragma omp parallel num_threads(numThreads) 30 | { 31 | Nodes th_nodes; 32 | #pragma omp for schedule(static) 33 | for (int i = 0; i < numThreads; i++) { 34 | Calc::Integrate_Serial(th_nodes, sim, t+i*sim.param.dt, isSol); 35 | nodes_par[numThreads - i - 1] = th_nodes; 36 | } 37 | } 38 | } 39 | nodes = nodes_par.back(); 40 | nodes_par.pop_back(); 41 | } 42 | else { 43 | Calc::Integrate_Serial(nodes, sim, t, isSol); 44 | } 45 | return; 46 | } 47 | 48 | void Calc::Integrate_Serial(Nodes& nodes, const Simdat& sim, const double t, const bool isSol) { 49 | if (sim.settings.compress) { 50 | Calc::GaussCompressIntegrate(nodes, sim, t, isSol); 51 | //If not solidifying, always choose the minimum of the two; otherwise, too expensive 52 | if (!isSol) { 53 | Nodes nodes_reg; 54 | Calc::GaussIntegrate(nodes_reg, sim, t, isSol); 55 | if (nodes_reg.size <= nodes.size) { nodes = nodes_reg; } 56 | } 57 | } 58 | else { 59 | Calc::GaussIntegrate(nodes, sim, t, isSol); 60 | } 61 | 62 | if (sim.domain.use_BCs) { Calc::AddBCs(nodes, sim.domain); } 63 | 64 | return; 65 | } 66 | 67 | void Calc::GaussIntegrate(Nodes& nodes, const Simdat& sim, const double t, const bool isSol) { 68 | 69 | // This enables the starting search path segment to be quickly initialized each time. 70 | static vector start_seg(sim.paths.size(), 1); 71 | 72 | // Quadrature node locations for order 2, 4, 8, and 16 73 | static const double locs[30] = { 74 | -0.57735027, 0.57735027, 75 | -0.86113631, -0.33998104, 0.33998104, 0.86113631, 76 | -0.96028986, -0.79666648, -0.52553241, -0.18343464, 0.18343464, 0.52553241, 0.79666648, 0.96028986, 77 | -0.98940093, -0.94457502, -0.8656312, -0.75540441, -0.61787624, -0.45801678, -0.28160355, -0.09501251, 0.09501251, 0.28160355, 0.45801678, 0.61787624, 0.75540441, 0.8656312, 0.94457502, 0.98940093 78 | }; 79 | 80 | // Quadrature weights for order 2, 4, 8, and 16 81 | static const double weights[30] = { 82 | 1.0, 1.0, 83 | 0.34785485, 0.65214515, 0.65214515, 0.34785485, 84 | 0.10122854, 0.22238103, 0.31370665, 0.36268378, 0.36268378, 0.31370665, 0.22238103, 0.10122854, 85 | 0.02715246, 0.06225352, 0.09515851, 0.12462897, 0.14959599, 0.16915652,0.18260342, 0.18945061, 0.18945061, 0.18260342, 0.16915652, 0.14959599,0.12462897, 0.09515851, 0.06225352, 0.02715246 86 | }; 87 | // For each beam and path 88 | for (int i = 0; i < sim.paths.size(); i++) { 89 | 90 | // Set beam, path, and seg_temp 91 | const Beam& beam = sim.beams[i]; 92 | const vector& path = sim.paths[i]; 93 | int seg_temp = start_seg[i]; 94 | 95 | // Get beta for beam 96 | const double beta = pow(3.0 / PI, 1.5) * beam.q / (sim.material.rho * sim.material.cps); 97 | 98 | // Keep incrementing up if t is greater than the end of the path segment but also below the end time of the scan 99 | while ((t > path[seg_temp].seg_time) && (seg_temp + 1 < path.size())) { seg_temp++; } 100 | // Keep incrementing down if t is less than end of previous path segment 101 | while ((t < path[seg_temp - 1].seg_time) && (seg_temp - 1 > 0)) { seg_temp--; } 102 | 103 | // If not solidifying, then make this the start next time the function is run 104 | if (!isSol) {start_seg[i] = seg_temp;} 105 | 106 | // Get minimim time to integrate to 107 | const double t0 = Util::t0calc(t, beam, sim.material, sim.settings); 108 | 109 | // Set maximum starting step size to nonDimensional diffusion time 110 | double curStep_max_start = beam.nond_dt; 111 | 112 | // If solidifying, refine integration time 113 | if (isSol) { curStep_max_start *= beam.az; } 114 | 115 | // Set max step size to starting step size 116 | double curStep_max = curStep_max_start; 117 | 118 | // Set step size to max step 119 | double curStep_use = curStep_max; 120 | 121 | // Start quadrature order at 16 122 | int curOrder = 16; 123 | 124 | // Make 1st segment at the exact time 125 | // Used for instantaneous heat source additon to laplacian 126 | int_seg current_beam = Util::GetBeamLoc(t, seg_temp, path, sim); 127 | current_beam.phix = (beam.ax * beam.ax + 0.0); 128 | current_beam.phiy = (beam.ay * beam.ay + 0.0); 129 | current_beam.phiz = (beam.az * beam.az + 0.0); 130 | current_beam.qmod *= beta; 131 | current_beam.dtau = 0.0; 132 | if (t <= path.back().seg_time && current_beam.qmod > 0.0) { Util::AddToNodes(nodes, current_beam); } 133 | 134 | bool tflag = true; 135 | double t2 = t; 136 | double t1 = t; 137 | double tpp = 0.0; 138 | 139 | if (t > path.back().seg_time) { 140 | tpp += t - path.back().seg_time; 141 | t2 = path.back().seg_time; 142 | } 143 | 144 | while (tpp >= 2 * curStep_max - curStep_max_start) { 145 | curStep_max *= 2.0; 146 | if (curOrder != 2) { 147 | curOrder = (curOrder / 2); 148 | } 149 | } 150 | 151 | while (tflag) { 152 | bool switchSeg = false; 153 | 154 | // Get maximum integration step for the segment 155 | const double ref_time = Util::GetRefTime(tpp, seg_temp, path, beam); 156 | 157 | // Sets step to the minimum of the ref time and max allowable time 158 | curStep_use = (ref_time < curStep_max) ? ref_time : curStep_max; 159 | 160 | // Set ending quadradure time based on step used 161 | t1 = t2 - curStep_use; 162 | 163 | // Time the next segment ends 164 | const double next_time = path[seg_temp - 1].seg_time; 165 | 166 | //If we are at the end of a segment, hit the end of it and set the program to jump to the next segment next time 167 | if (t1 <= next_time) { 168 | t1 = next_time; 169 | // If next time is greater than t0, switch segments and keep going 170 | if (next_time > t0) { switchSeg = true; } 171 | // Otherwise, it should end 172 | else { tflag = false; } 173 | } 174 | 175 | //Add Quadrature Points 176 | double tau, ct; 177 | for (int a = (2 * curOrder - 3); a > (curOrder - 3); a--) { 178 | double tp = 0.5 * ((t2 - t1) * locs[a] + (t2 + t1)); 179 | tau = t - tp; 180 | ct = 12.0 * sim.material.a * tau; 181 | 182 | current_beam = Util::GetBeamLoc(tp, seg_temp, path, sim); 183 | current_beam.phix = (beam.ax * beam.ax + ct); 184 | current_beam.phiy = (beam.ay * beam.ay + ct); 185 | current_beam.phiz = (beam.az * beam.az + ct); 186 | current_beam.qmod *= beta; 187 | current_beam.dtau = 0.5 * (t2 - t1) * weights[a]; 188 | 189 | if (current_beam.qmod > 0.0 && current_beam.dtau > 0.0) { Util::AddToNodes(nodes, current_beam); } 190 | } 191 | 192 | //If we are switching segments, increment the start segment down 193 | if (switchSeg) {seg_temp--;} 194 | 195 | // Increment the total time passed 196 | tpp += (t2 - t1); 197 | 198 | // Set start quadruatre time to current end quadrature time 199 | t2 = t1; 200 | 201 | //Increase Maximum Step and Decrease Gauss order if it is okay to do so 202 | if (tpp >= 2 * curStep_max - curStep_max_start) { 203 | curStep_max *= 2.0; 204 | if (curOrder != 2) { 205 | curOrder = (curOrder / 2); 206 | } 207 | } 208 | } 209 | } 210 | return; 211 | } 212 | 213 | void Calc::GaussCompressIntegrate(Nodes& nodes, const Simdat& sim, const double t, const bool isSol) { 214 | // This enables the starting search path segment to be quickly initialized each time. 215 | static vector start_seg(sim.paths.size(), 1); 216 | 217 | // Quadrature node locations for order 2, 4, 8, and 16 218 | static const double locs[30] = { 219 | -0.57735027, 0.57735027, 220 | -0.86113631, -0.33998104, 0.33998104, 0.86113631, 221 | -0.96028986, -0.79666648, -0.52553241, -0.18343464, 0.18343464, 0.52553241, 0.79666648, 0.96028986, 222 | -0.98940093, -0.94457502, -0.8656312, -0.75540441, -0.61787624, -0.45801678, -0.28160355, -0.09501251, 0.09501251, 0.28160355, 0.45801678, 0.61787624, 0.75540441, 0.8656312, 0.94457502, 0.98940093 223 | }; 224 | 225 | // Quadrature weights for order 2, 4, 8, and 16 226 | static const double weights[30] = { 227 | 1.0, 1.0, 228 | 0.34785485, 0.65214515, 0.65214515, 0.34785485, 229 | 0.10122854, 0.22238103, 0.31370665, 0.36268378, 0.36268378, 0.31370665, 0.22238103, 0.10122854, 230 | 0.02715246, 0.06225352, 0.09515851, 0.12462897, 0.14959599, 0.16915652,0.18260342, 0.18945061, 0.18945061, 0.18260342, 0.16915652, 0.14959599,0.12462897, 0.09515851, 0.06225352, 0.02715246 231 | }; 232 | 233 | // For each beam and path 234 | for (int i = 0; i < sim.paths.size(); i++) { 235 | 236 | // Set beam, path, and seg_temp 237 | const Beam& beam = sim.beams[i]; 238 | const vector& path = sim.paths[i]; 239 | int seg_temp = start_seg[i]; 240 | 241 | // Get beta for beam 242 | const double beta = pow(3.0 / PI, 1.5) * beam.q / (sim.material.rho * sim.material.cps); 243 | 244 | // Keep incrementing up if t is greater than the end of the path segment but also below the end time of the scan 245 | while ((t > path[seg_temp].seg_time) && (seg_temp + 1 < path.size())) { seg_temp++; } 246 | // Keep incrementing down if t is less than end of previous path segment 247 | while ((t < path[seg_temp - 1].seg_time) && (seg_temp - 1 > 0)) { seg_temp--; } 248 | 249 | // If not solidifying, then make this the start next time the function is run 250 | if (!isSol) { start_seg[i] = seg_temp; } 251 | 252 | // Get minimim time to integrate to 253 | const double t0 = Util::t0calc(t, beam, sim.material, sim.settings); 254 | 255 | // Set maximum starting step size to nonDimensional diffusion time 256 | double curStep_max_start = beam.nond_dt; 257 | 258 | // If solidifying, refine integration time 259 | if (isSol) { curStep_max_start *= beam.az; } 260 | 261 | // Set max step size to starting step size 262 | double curStep_max = curStep_max_start; 263 | 264 | // Set step size to max step 265 | double curStep_use = curStep_max; 266 | 267 | // Start quadrature order at 16 268 | int curOrder = 16; 269 | 270 | // Make 1st segment at the exact time 271 | // Used for instantaneous heat source additon to laplacian 272 | int_seg current_beam = Util::GetBeamLoc(t, seg_temp, path, sim); 273 | current_beam.phix = (beam.ax * beam.ax + 0.0); 274 | current_beam.phiy = (beam.ay * beam.ay + 0.0); 275 | current_beam.phiz = (beam.az * beam.az + 0.0); 276 | current_beam.qmod *= beta; 277 | current_beam.dtau = 0.0; 278 | if (t <= path.back().seg_time && current_beam.qmod>0.0) { Util::AddToNodes(nodes, current_beam); } 279 | 280 | bool tflag = true; 281 | double t2 = t; 282 | double t1 = t; 283 | double tpp = 0.0; 284 | 285 | if (t > path.back().seg_time) { 286 | tpp += t - path.back().seg_time; 287 | t2 = path.back().seg_time; 288 | } 289 | 290 | while (tpp >= 2 * curStep_max - curStep_max_start) { 291 | curStep_max *= 2.0; 292 | if (curOrder != 2) { 293 | curOrder = (curOrder / 2); 294 | } 295 | } 296 | 297 | while (tflag) { 298 | 299 | //Compression variables 300 | double r2, dist2, xp, yp, xs, ys, dx, dy, ts, dt; 301 | double sum_t = 0, sum_qmodt = 0, sum_qmodtx = 0, sum_qmodty = 0; //sums of t, qmod*t, qmod*t*x,... to find centers 302 | int seg_temp_2; 303 | int num_comb_segs = 0; //number of segments to be combined 304 | 305 | //If the time is less than t0, break the whole thing 306 | int quit = 0; 307 | //If outside r, then keep going down until back in r 308 | //NOTE:Only looks at end of scan path...good for points but not lines 309 | while (true) { 310 | xs = path[seg_temp].sx; 311 | ys = path[seg_temp].sy; 312 | ts = path[seg_temp].seg_time; 313 | if (ts <= t0) { quit = 1; break; } 314 | // If in R, quit loop 315 | if (Util::InRMax(xs, ys, sim.domain, sim.settings)) { break; } 316 | // If outside R, add cumulative time, set time to be end of segment 317 | else { tpp += t2 - ts; t2 = ts; } 318 | // if (!Util::InRMax(xs, ys, sim)) { seg_temp--; } 319 | // else { tpp += t2 - ts; spp = tpp / sim.util.nond_dt; t2 = ts; break; } 320 | } 321 | if (quit) { break; } 322 | 323 | while (tpp >= 2 * curStep_max - curStep_max_start) { 324 | curStep_max *= 2.0; 325 | if (curOrder != 2) { 326 | curOrder = (curOrder / 2); 327 | } 328 | } 329 | 330 | double ref_time = Util::GetRefTime(tpp, seg_temp, path, beam); 331 | if (ref_time < curStep_max) { curStep_use = ref_time; } 332 | else { curStep_use = curStep_max; } 333 | 334 | int_seg current_beam_t2 = Util::GetBeamLoc(t2, seg_temp, path, sim); 335 | xp = current_beam_t2.xb; 336 | yp = current_beam_t2.yb; 337 | 338 | // Diffusion Distance Squared (Distance it diffused by *some amount*, squared) 339 | r2 = log(2.0) / 8.0 * (beam.ax * beam.ax) * (12.0 * (t - t2) * sim.material.a / (beam.ax * beam.ax) + 1.0); 340 | 341 | seg_temp_2 = seg_temp; 342 | 343 | t1 = path[seg_temp - 1].seg_time; 344 | 345 | //If we won't be switching segments, do normal integration 346 | if (t1 < t2 - curStep_use) { 347 | num_comb_segs = 0; 348 | t1 = t2 - curStep_use; 349 | } 350 | else { 351 | bool cflag = true; 352 | while (cflag) { //If we will be switching segments, do compressed integration 353 | //If the segment start time is zero, end the loop 354 | if (path[seg_temp_2 - 1].seg_time <= t0) { tflag = 0; break; } 355 | 356 | xs = path[seg_temp_2 - 1].sx; 357 | ys = path[seg_temp_2 - 1].sy; 358 | dist2 = (xs - xp) * (xs - xp) + (ys - yp) * (ys - yp); 359 | ts = path[seg_temp_2 - 1].seg_time; 360 | 361 | // If the next segment is outside the calculation domain, break the loop 362 | // NOTE:Only looks at end of scan path...good for points but not lines 363 | if (!Util::InRMax(xp, yp, sim.domain, sim.settings)) { break; } 364 | 365 | // IF 366 | // the distance between endpoints, or points, is bigger than the diffusion distance 367 | // OR 368 | // the qmod's aren't equal (discontinuity) AND current order or time difference is *as defined* (to minimize error) 369 | // THEN 370 | // stop combining segments and finalize 371 | // ELSE 372 | // If the distance of the next segment is less than the diffusion distance, set end time to next segment, average them, and keep going 373 | if ((dist2 > r2) || (path[seg_temp_2].sqmod != path[seg_temp_2 - 1].sqmod && (curOrder > 2 || (t2 - ts) > (curStep_max / 64.0)))) { 374 | //If point, do averaging calculations and set new end time to the next segment 375 | if (path[seg_temp_2].smode) { 376 | sum_qmodtx += path[seg_temp_2].sx * path[seg_temp_2].sqmod * (t1 - ts); 377 | sum_qmodty += path[seg_temp_2].sy * path[seg_temp_2].sqmod * (t1 - ts); 378 | sum_qmodt += path[seg_temp_2].sqmod * (t1 - ts); 379 | sum_t += (t1 - ts); 380 | t1 = ts; 381 | } 382 | //If line, find the time when the dist=r. If this time is less than the next segment start time, set end time to next segment; else, set start time to when dist=r 383 | else { 384 | dx = path[seg_temp_2].sx - path[seg_temp_2 - 1].sx; 385 | dy = path[seg_temp_2].sy - path[seg_temp_2 - 1].sy; 386 | dt = path[seg_temp_2].seg_time - ts; 387 | 388 | double t_int = ts + dt * (sqrt(((xp - xs) * (xp - xs) + (yp - ys) * (yp - ys)) / (dx * dx + dy * dy)) - sqrt(r2 / (dx * dx + dy * dy))); 389 | if (t_int < t2 - curStep_max) { t_int = t2 - curStep_max; } 390 | sum_qmodtx += (xs + dx * ((t1 + t_int) / 2.0 - ts) / dt) * path[seg_temp_2].sqmod * (t1 - t_int); 391 | sum_qmodty += (ys + dy * ((t1 + t_int) / 2.0 - ts) / dt) * path[seg_temp_2].sqmod * (t1 - t_int); 392 | sum_qmodt += path[seg_temp_2].sqmod * (t1 - t_int); 393 | sum_t += (t1 - t_int); 394 | t1 = t_int; 395 | } 396 | cflag = false; 397 | } 398 | else { 399 | //If the next time is less than the minimum allowed time, set appropriate times to the minimum allowed time 400 | if (ts < t2 - curStep_max) { 401 | cflag = false; 402 | if (path[seg_temp_2].smode) { t1 = t2 - curStep_max; } 403 | else { t1 = t2 - curStep_max; } 404 | } 405 | //Otherwise combine the segments and continue 406 | else { 407 | t1 = ts; 408 | num_comb_segs++; 409 | } 410 | 411 | //If point, 412 | if (path[seg_temp_2].smode) { 413 | dt = (path[seg_temp_2].seg_time - t1); 414 | 415 | sum_qmodtx += path[seg_temp_2].sx * path[seg_temp_2].sqmod * dt; 416 | sum_qmodty += path[seg_temp_2].sy * path[seg_temp_2].sqmod * dt; 417 | sum_qmodt += path[seg_temp_2].sqmod * dt; 418 | sum_t += dt; 419 | } 420 | else { 421 | dx = path[seg_temp_2].sx - path[seg_temp_2 - 1].sx; 422 | dy = path[seg_temp_2].sy - path[seg_temp_2 - 1].sy; 423 | dt = (path[seg_temp_2].seg_time - t1); 424 | 425 | sum_qmodtx += (xs + dx * ((path[seg_temp_2].seg_time + t1) / 2.0 - ts) / dt) * path[seg_temp_2].sqmod * dt; 426 | sum_qmodty += (ys + dy * ((path[seg_temp_2].seg_time + t1) / 2.0 - ts) / dt) * path[seg_temp_2].sqmod * dt; 427 | sum_qmodt += path[seg_temp_2].sqmod * dt; 428 | sum_t += dt; 429 | } 430 | } 431 | if (t1 == path[seg_temp_2 - 1].seg_time) { 432 | xs = path[seg_temp_2 - 1].sx; 433 | ys = path[seg_temp_2 - 1].sy; 434 | seg_temp_2--; 435 | if (!Util::InRMax(xs, ys, sim.domain, sim.settings)) { break; } 436 | } 437 | } 438 | 439 | if (!num_comb_segs) { //If we didn't combine any segments anyways, then just do normal integration 440 | t1 = path[seg_temp - 1].seg_time; 441 | seg_temp_2 = seg_temp - 1; 442 | } 443 | } 444 | 445 | //Add Quadrature Points using the same average for x, y, and qmod (CAN BE IMPROVED) 446 | double tau, ct; 447 | if (num_comb_segs) { 448 | int_seg current_beam; 449 | if (sum_qmodt > 0.0) { 450 | current_beam.xb = sum_qmodtx / sum_qmodt; 451 | current_beam.yb = sum_qmodty / sum_qmodt; 452 | current_beam.qmod = sum_qmodt / sum_t; 453 | for (int a = (2 * curOrder - 3); a > (curOrder - 3); a--) { 454 | double tp = 0.5 * ((t2 - t1) * locs[a] + (t2 + t1)); 455 | tau = t - tp; 456 | ct = 12.0 * sim.material.a * tau; 457 | 458 | current_beam.phix = (beam.ax * beam.ax + ct); 459 | current_beam.phiy = (beam.ay * beam.ay + ct); 460 | current_beam.phiz = (beam.az * beam.az + ct); 461 | current_beam.qmod *= beta; 462 | current_beam.dtau = 0.5 * (t2 - t1) * weights[a]; 463 | if ((current_beam.qmod > 0.0) && (current_beam.dtau > 0.0)) { Util::AddToNodes(nodes, current_beam); } 464 | } 465 | } 466 | 467 | } 468 | //Add Quadrature Points 469 | else { 470 | for (int a = (2 * curOrder - 3); a > (curOrder - 3); a--) { 471 | double tp = 0.5 * ((t2 - t1) * locs[a] + (t2 + t1)); 472 | tau = t - tp; 473 | ct = 12.0 * sim.material.a * tau; 474 | 475 | int_seg current_beam = Util::GetBeamLoc(tp, seg_temp, path, sim); 476 | current_beam.phix = (beam.ax * beam.ax + ct); 477 | current_beam.phiy = (beam.ay * beam.ay + ct); 478 | current_beam.phiz = (beam.az * beam.az + ct); 479 | current_beam.qmod *= beta; 480 | current_beam.dtau = 0.5 * (t2 - t1) * weights[a]; 481 | if ((current_beam.qmod > 0.0) && (current_beam.dtau > 0.0)) { Util::AddToNodes(nodes, current_beam); } 482 | } 483 | } 484 | 485 | tpp += t2 - t1; 486 | t2 = t1; 487 | 488 | //Increase Maximum Step and Decrease Gauss order if it is "safe" to 489 | if (tpp >= 2 * curStep_max - curStep_max_start) { 490 | curStep_max *= 2.0; 491 | if (curOrder != 2) { 492 | curOrder = (curOrder / 2); 493 | } 494 | } 495 | 496 | seg_temp = seg_temp_2; 497 | if (t1 <= t0) { tflag = 0; } 498 | } 499 | } 500 | return; 501 | } 502 | 503 | void Calc::AddBCs(Nodes& nodes, const Domain& domain) { 504 | 505 | vector> allCoords; 506 | vector> newCoords; 507 | vector coords = { 0,0,0 }; 508 | newCoords.push_back(coords); 509 | allCoords.push_back(coords); 510 | 511 | double xmin, xmax; 512 | double ymin, ymax; 513 | double zmin, zmax; 514 | 515 | xmin = domain.BC_xmin; 516 | xmax = domain.BC_xmax; 517 | ymin = domain.BC_ymin; 518 | ymax = domain.BC_ymax; 519 | zmin = domain.BC_zmin; 520 | zmax = domain.zmax; 521 | 522 | vector> checkCoords = newCoords; 523 | newCoords.clear(); 524 | // Strength is how many reflections to use 525 | int iter = 0; int strength = 0; 526 | if (domain.BC_reflections != INT_MAX) { strength = domain.BC_reflections; } 527 | while (checkCoords.size() > 0 && iter < strength) { 528 | for (int i = 0; i < checkCoords.size(); i++) { 529 | if (xmin != DBL_MAX) { 530 | coords = checkCoords[i]; 531 | coords[0] = (-1 - coords[0]); 532 | if (std::find(allCoords.begin(), allCoords.end(), coords) == allCoords.end()) {; 533 | allCoords.push_back(coords); 534 | newCoords.push_back(coords); 535 | } 536 | }; 537 | if (xmax != DBL_MAX) { 538 | coords = checkCoords[i]; 539 | coords[0] = (1 - coords[0]); 540 | if (std::find(allCoords.begin(), allCoords.end(), coords) == allCoords.end()) { 541 | allCoords.push_back(coords); 542 | newCoords.push_back(coords); 543 | } 544 | }; 545 | if (ymin != DBL_MAX) { 546 | coords = checkCoords[i]; 547 | coords[1] = (-1 - coords[1]); 548 | if (std::find(allCoords.begin(), allCoords.end(), coords) == allCoords.end()) { 549 | allCoords.push_back(coords); 550 | newCoords.push_back(coords); 551 | } 552 | }; 553 | if (ymax != DBL_MAX) { 554 | coords = checkCoords[i]; 555 | coords[1] = (1 - coords[1]); 556 | if (std::find(allCoords.begin(), allCoords.end(), coords) == allCoords.end()) { 557 | allCoords.push_back(coords); 558 | newCoords.push_back(coords); 559 | } 560 | }; 561 | if (zmin != DBL_MAX) { 562 | coords = checkCoords[i]; 563 | coords[2] = (-1 - coords[2]); 564 | if (std::find(allCoords.begin(), allCoords.end(), coords) == allCoords.end()) { 565 | allCoords.push_back(coords); 566 | newCoords.push_back(coords); 567 | } 568 | }; 569 | if (zmax != DBL_MAX) { 570 | coords = checkCoords[i]; 571 | coords[2] = (1 - coords[2]); 572 | 573 | if (std::find(allCoords.begin(), allCoords.end(), coords) == allCoords.end() && (coords[2]!=1)) { 574 | allCoords.push_back(coords); 575 | newCoords.push_back(coords); 576 | } 577 | }; 578 | } 579 | checkCoords = newCoords; 580 | newCoords.clear(); 581 | iter += 1; 582 | } 583 | 584 | int org_size = nodes.size; 585 | Nodes nodes_org = nodes; 586 | Util::ClearNodes(nodes); 587 | 588 | // Now just convert between refCoords and realCoords 589 | for (int i = 0; i < allCoords.size(); i++) { 590 | int xRef = allCoords[i][0]; 591 | int yRef = allCoords[i][1]; 592 | int zRef = allCoords[i][2]; 593 | 594 | int nX = std::abs(xRef % 2); 595 | int nY = std::abs(yRef % 2); 596 | int nZ = std::abs(zRef % 2); 597 | 598 | Nodes nodes2 = nodes_org; 599 | for (int i = 0; i < org_size; i++) { 600 | nodes2.xb[i] = (xRef + nX) * xmax - (xRef - nX) * xmin + (1 - 2 * nX) * nodes2.xb[i]; 601 | nodes2.yb[i] = (yRef + nY) * ymax - (yRef - nY) * ymin + (1 - 2 * nY) * nodes2.yb[i]; 602 | nodes2.zb[i] = (zRef + nZ) * zmax - (zRef - nZ) * zmin + (1 - 2 * nZ) * nodes2.zb[i]; 603 | } 604 | Util::CombineNodes(nodes, nodes2); 605 | } 606 | 607 | return; 608 | } -------------------------------------------------------------------------------- /src/Init.cpp: -------------------------------------------------------------------------------- 1 | /**************************************************************************** 2 | * Copyright (c) 2019 UT-Battelle, LLC * 3 | * All rights reserved. * 4 | * * 5 | * This file is part of 3dThesis. 3dThesis is distributed under a * 6 | * BSD 3-clause license. For the licensing terms see the LICENSE file in * 7 | * the top-level directory. * 8 | * * 9 | * SPDX-License-Identifier: BSD-3-Clause * 10 | ****************************************************************************/ 11 | 12 | #include 13 | #include 14 | #include 15 | #include 16 | #include 17 | #include 18 | #include 19 | #include 20 | 21 | #include "DataStructs.h" 22 | #include "Init.h" 23 | #include "Util.h" 24 | 25 | #ifdef _WIN32 // conditional needed to prevent error when compiling for unix systems 26 | #include 27 | #else // conditional for unix systems; should work *most of the time 28 | #include 29 | #include 30 | #include 31 | #endif 32 | 33 | void Init::Keywords_Lv1(vector& mainWords, vector& isIn, const string& input_file) { 34 | string line; 35 | std::ifstream readFile; 36 | readFile.exceptions(std::ifstream::failbit | std::ifstream::badbit); 37 | try { 38 | readFile.open(input_file.c_str(), std::ios::in); 39 | while (true) { 40 | string tempWord = ""; 41 | string tempWord2 = ""; 42 | int mainWord = -1; 43 | string subWord = ""; 44 | readFile >> tempWord; getline(readFile, line); 45 | for (int i = 0; i < mainWords.size(); i++) { 46 | if (tempWord == mainWords[i]) { 47 | mainWord = i; 48 | isIn[i] += 1; 49 | break; 50 | } 51 | } 52 | if (mainWord >= 0) { 53 | readFile >> tempWord; getline(readFile, line); 54 | if (tempWord == "{") { 55 | while (true) { 56 | readFile >> tempWord; 57 | if (tempWord != "}") { 58 | readFile >> tempWord2; getline(readFile, line); 59 | } 60 | else { getline(readFile, line); break; } 61 | } 62 | } 63 | } 64 | } 65 | } 66 | catch (const std::ifstream::failure&) {} 67 | catch (const std::exception& e) {} 68 | } 69 | 70 | void Init::catchPrint(const string& input_file, const int num_read, const bool print){ 71 | if (print) { 72 | if (num_read) { std::cout << "Finished Reading " << input_file << std::endl;} 73 | else { std::cout << "Failed to Open " << input_file << std::endl;} 74 | } 75 | } 76 | 77 | void Init::Keywords_Lv2(vector& mainWords, vector>& subWords, vector>& strings, const string& input_file, const bool print) { 78 | 79 | for (int i = 0; i < subWords.size(); i++) { 80 | for (int j = 0; j < subWords[i].size(); j++) { 81 | strings[i].push_back(""); 82 | } 83 | } 84 | 85 | string line; 86 | std::ifstream readFile; 87 | int num_read = 0; 88 | readFile.exceptions(std::ifstream::failbit | std::ifstream::badbit); 89 | try { 90 | readFile.open(input_file.c_str(), std::ios::in); 91 | while (true) { 92 | string tempWord = ""; 93 | string tempWord2 = ""; 94 | int mainWord = -1; 95 | string subWord = ""; 96 | readFile >> tempWord; getline(readFile, line); 97 | for (int i = 0; i < mainWords.size(); i++) { 98 | if (tempWord == mainWords[i]) { 99 | mainWord = i; 100 | break; 101 | } 102 | } 103 | if (mainWord >= 0) { 104 | readFile >> tempWord; getline(readFile, line); 105 | if (tempWord == "{") { 106 | while (true) { 107 | readFile >> tempWord; 108 | if (tempWord != "}") { 109 | readFile >> tempWord2; getline(readFile, line); 110 | for (int i = 0; i < subWords[mainWord].size(); i++) { 111 | if (tempWord == subWords[mainWord][i]) { 112 | strings[mainWord][i] = tempWord2; 113 | num_read++; 114 | } 115 | } 116 | } 117 | else { getline(readFile, line); break; } 118 | } 119 | } 120 | } 121 | } 122 | } 123 | catch (const std::ifstream::failure&) { 124 | catchPrint(input_file, num_read, print); 125 | readFile.close(); 126 | } 127 | catch (const std::exception& e) { 128 | catchPrint(input_file, num_read, print); 129 | readFile.close(); 130 | } 131 | } 132 | 133 | void Init::Keywords_Lv2(vector& mainWords, vector>& subWords, vector>& values, const string& input_file, const bool print) { 134 | 135 | for (int i = 0; i < subWords.size(); i++) { 136 | for (int j = 0; j < subWords[i].size(); j++) { 137 | values[i].push_back(DBL_MAX); 138 | } 139 | } 140 | 141 | string line; 142 | std::ifstream readFile; 143 | int num_read = 0; 144 | readFile.exceptions(std::ifstream::failbit | std::ifstream::badbit); 145 | try { 146 | readFile.open(input_file.c_str(), std::ios::in); 147 | while (true) { 148 | string tempWord = ""; 149 | double tempValue = DBL_MAX; 150 | int mainWord = -1; 151 | string subWord = ""; 152 | readFile >> tempWord; getline(readFile, line); 153 | for (int i = 0; i < mainWords.size(); i++) { 154 | if (tempWord == mainWords[i]) { 155 | mainWord = i; 156 | break; 157 | } 158 | } 159 | if (mainWord >= 0) { 160 | readFile >> tempWord; getline(readFile, line); 161 | if (tempWord == "{") { 162 | while (true) { 163 | readFile >> tempWord; 164 | if (tempWord != "}") { 165 | readFile >> tempValue; getline(readFile, line); 166 | for (int i = 0; i < subWords[mainWord].size(); i++) { 167 | if (tempWord == subWords[mainWord][i]) { 168 | values[mainWord][i] = tempValue; 169 | num_read++; 170 | } 171 | } 172 | } 173 | else { getline(readFile, line); break; } 174 | } 175 | } 176 | } 177 | } 178 | } 179 | catch (const std::ifstream::failure&) { 180 | catchPrint(input_file, num_read, print); 181 | readFile.close(); 182 | } 183 | catch (const std::exception& e) { 184 | catchPrint(input_file, num_read, print); 185 | readFile.close(); 186 | } 187 | } 188 | 189 | void Init::SetValues(string& simValue, string input, string simDefault, string name, int err, const bool print) { 190 | if (input == string("")) { 191 | simValue = simDefault; 192 | setPrint(simDefault, name, err, print); 193 | } 194 | else { simValue = input; } 195 | return; 196 | } 197 | void Init::SetValues(int& simValue, string input, int simDefault, string name, int err, const bool print) { 198 | if (input == string("")) { 199 | simValue = simDefault; 200 | setPrint(simDefault, name, err, print); 201 | } 202 | else { 203 | simValue = std::stoi(input); 204 | } 205 | return; 206 | } 207 | void Init::SetValues(bool& simValue, string input, bool simDefault, string name, int err, const bool print) { 208 | if (input == string("")) { 209 | simValue = simDefault; 210 | setPrint(simDefault, name, err, print); 211 | } 212 | else { simValue = bool(std::stoi(input)); } 213 | return; 214 | } 215 | void Init::SetValues(double& simValue, string input, double simDefault, string name, int err, const bool print) { 216 | if (input == string("")) { 217 | simValue = simDefault; 218 | Init::setPrint(simDefault, name, err, print); 219 | } 220 | else { simValue = std::stod(input); } 221 | return; 222 | } 223 | 224 | void Init::checkAsterisks(const std::string s, const std::string del, const std::string file, const bool print) { 225 | // If there are multiple '*', there is a problem 226 | if (s.find(del) != string::npos) { 227 | if (print) 228 | std::cout << "Too many '*' in: " << file << "\n"; 229 | exit(1); 230 | } 231 | } 232 | 233 | void Init::GetFileNames(FileNames& files, const string& file, const bool print) { 234 | 235 | size_t pos = file.find_last_of("/"); 236 | files.dataDir = "Data"; 237 | if (pos != string::npos) { files.dataDir = file.substr(0, pos) + "/Data";} 238 | Init::MakeDataDirectory(files.dataDir); 239 | 240 | vector mainWords; 241 | mainWords.push_back("Simulation"); 242 | mainWords.push_back("Options"); 243 | //mainWords.push_back("Utility"); 244 | 245 | vector> subWords(mainWords.size()); 246 | subWords[0].push_back("Name"); 247 | subWords[0].push_back("Mode"); 248 | subWords[0].push_back("Material"); 249 | subWords[0].push_back("Beam"); 250 | subWords[0].push_back("Path"); 251 | 252 | subWords[1].push_back("Domain"); 253 | subWords[1].push_back("Output"); 254 | subWords[1].push_back("Settings"); 255 | 256 | //subWords[2].push_back("Points"); 257 | //subWords[2].push_back("ParBeams"); 258 | //subWords[2].push_back("InfBeams"); 259 | 260 | vector> values(mainWords.size()); 261 | 262 | Init::Keywords_Lv2(mainWords, subWords, values, file, print); 263 | 264 | Init::SetValues(files.name, values[0][0], "N/A", "Simulation Name", 2, print); 265 | Init::SetValues(files.mode, values[0][1], "", "Mode File", 2, print); 266 | Init::SetValues(files.material, values[0][2], "", "Material File", 2, print); 267 | Init::SetValues(files.beam, values[0][3], "", "Beam File", 2, print); 268 | Init::SetValues(files.path, values[0][4], "", "Path File", 2, print); 269 | 270 | 271 | Init::SetValues(files.domain, values[1][0], "", "Domain File", 0, print); 272 | Init::SetValues(files.output, values[1][1], "", "Output File", 0, print); 273 | Init::SetValues(files.settings, values[1][2], "", "Settings File", 0, print); 274 | 275 | //Init::SetValues(sim.files.points, files[2][0], "", "Points File", 0, print); 276 | //Init::SetValues(sim.files.parBeams, files[2][1], "", "ParBeams File", 0, print); 277 | //Init::SetValues(sim.files.infBeams, files[2][2], "", "InfBeams File", 0, print); 278 | 279 | return; 280 | } 281 | void Init::MakeDataDirectory(const string& file) { 282 | //Make Data directory if it does not already exist 283 | #if defined(_WIN32) 284 | _mkdir(file.c_str()); 285 | #else 286 | mkdir(file.c_str(), 0777); // notice that 777 is different than 0777 287 | #endif 288 | } 289 | 290 | void Init::ReadSimParams(Simdat& sim) { 291 | 292 | Init::FileRead_Beams(sim.beams, sim.files.beam, sim.print); // Read beam files 293 | Init::FileRead_Material(sim.material, sim.files.material, sim.print); Util::Calc_NonD_dt(sim.beams, sim.material); // Read material properties // Calculate nonDimensional integration time for all beams 294 | Init::FileRead_Paths(sim.paths, sim.files.path, sim.print); Util::Calc_AllScansEndTime(sim); // Read path files // Calculate Important Simulation Parameters 295 | 296 | Init::FileRead_Mode(sim, sim.files.mode); // Initialize simulation mode 297 | 298 | Init::FileRead_Domain(sim.domain, sim.files.domain, sim.print); Util::Calc_ScanBounds(sim.domain, sim.paths); Init::SetDomainParams(sim.domain); // Read domain // Look at bounds of scan path and make domain bounds if bounds are unspecified // Set domain parameters 299 | Init::FileRead_Output(sim.output, sim.files.output, sim.print); // Read what to output 300 | Init::FileRead_Settings(sim.settings, sim.files.settings, sim.print); Util::Calc_RMax(sim); // Read fine tuned settings // Calculate maximum radius to care about 301 | 302 | return; 303 | } 304 | 305 | void Init::FileRead_Mode(Simdat& sim, const string& file) { 306 | vector mainWords; 307 | mainWords.push_back("Snapshots"); 308 | mainWords.push_back("Solidification"); 309 | 310 | vector hasMainWords(mainWords.size()); 311 | Keywords_Lv1(mainWords, hasMainWords, file); 312 | 313 | int sum = 0; 314 | int loc = -1; 315 | for (int i = 0; i < hasMainWords.size(); i++) { 316 | sum += hasMainWords[i]; 317 | if (hasMainWords[i] > 0) {loc = i;} 318 | } 319 | 320 | if (sum > 1) { 321 | if (sim.print) 322 | std::cout << "Fatal Error: Too many modes in file " << file << std::endl; 323 | exit(1); 324 | } 325 | if (loc < 0) { 326 | if (sim.print) 327 | std::cout << "Fatal Error: No modes in file " << file << std::endl; 328 | exit(1); 329 | } 330 | 331 | sim.param.mode = ""; 332 | switch (loc) { 333 | case 0: 334 | FileRead_Mode_Snapshot(sim, file); 335 | break; 336 | case 1: 337 | FileRead_Mode_Solidification(sim, file); 338 | break; 339 | } 340 | } 341 | void Init::FileRead_Mode_Snapshot(Simdat& sim, const string& file) { 342 | sim.param.mode = "Snapshots"; 343 | 344 | vector mainWords; 345 | mainWords.push_back("Snapshots"); 346 | 347 | vector> subWords(mainWords.size()); 348 | subWords[0].push_back("Times"); 349 | subWords[0].push_back("ScanFracs"); 350 | subWords[0].push_back("Tracking"); 351 | 352 | vector> values(mainWords.size()); 353 | 354 | Init::Keywords_Lv2(mainWords, subWords, values, file, sim.print); 355 | 356 | if (values[0][0].size() && values[0][1].size()) { 357 | if (sim.print) 358 | std::cout << "Fatal Error: Too many time types for snapshot in " << file << std::endl; 359 | exit(1); 360 | } 361 | 362 | // For Times 363 | if (values[0][0].size()) { 364 | string s = values[0][0]; 365 | string tmp; 366 | stringstream ss(s); 367 | while (getline(ss, tmp, ',')) { 368 | sim.param.SnapshotTimes.push_back(std::stod(tmp)); 369 | } 370 | } 371 | 372 | // For ScanFracs 373 | if (values[0][1].size()) { 374 | string s = values[0][1]; 375 | string tmp; 376 | stringstream ss(s); 377 | while (getline(ss, tmp, ',')) { 378 | sim.param.SnapshotTimes.push_back(std::stod(tmp)/100.0*sim.util.allScansEndTime); 379 | } 380 | } 381 | 382 | Init::SetValues(sim.param.tracking, values[0][2], string("None"), "Tracking", 0, sim.print); 383 | 384 | } 385 | void Init::FileRead_Mode_Solidification(Simdat& sim, const string& file) { 386 | sim.param.mode = "Solidification"; 387 | 388 | vector mainWords; 389 | mainWords.push_back("Solidification"); 390 | 391 | vector> subWords(mainWords.size()); 392 | 393 | subWords[0].push_back("Tracking"); // None, Volume, Perimeter 394 | subWords[0].push_back("Timestep"); 395 | subWords[0].push_back("OutputFrequency"); 396 | subWords[0].push_back("CheckRadius"); 397 | subWords[0].push_back("Secondary"); 398 | 399 | vector> values(mainWords.size()); 400 | 401 | Init::Keywords_Lv2(mainWords, subWords, values, file, sim.print); 402 | 403 | Init::SetValues(sim.param.tracking, values[0][0], string("None"), "Tracking", 0, sim.print); 404 | Init::SetValues(sim.param.dt, values[0][1], 1e-5, "Timestep", 0, sim.print); 405 | Init::SetValues(sim.param.out_freq, values[0][2], INT_MAX, "Output Frequency", 0, sim.print); 406 | Init::SetValues(sim.param.radiusCheck, values[0][3], 1.0, "Check Radius", 0, sim.print); 407 | Init::SetValues(sim.param.secondary, values[0][4], 0, "Secondary Solidfication", 0, sim.print); 408 | } 409 | 410 | void Init::FileRead_Material(Material& material, const string& file, const bool print) { 411 | vector mainWords; 412 | mainWords.push_back("Constants"); 413 | mainWords.push_back("CET"); 414 | 415 | vector> subWords(mainWords.size()); 416 | subWords[0].push_back("T_0"); 417 | subWords[0].push_back("T_L"); 418 | subWords[0].push_back("k"); 419 | subWords[0].push_back("c"); 420 | subWords[0].push_back("p"); 421 | 422 | subWords[1].push_back("N0"); 423 | subWords[1].push_back("n"); 424 | subWords[1].push_back("a"); 425 | 426 | vector> values(mainWords.size()); 427 | 428 | Init::Keywords_Lv2(mainWords, subWords, values, file, print); 429 | 430 | Init::SetValues(material.T_init, values[0][0], 1273.0, "Initial Temperature", 1, print); 431 | Init::SetValues(material.T_liq, values[0][1], 1610.0, "Liquidus Temperature", 1, print); 432 | Init::SetValues(material.kon, values[0][2], 26.6, "Thermal Conductivity", 1, print); 433 | Init::SetValues(material.cps, values[0][3], 600.00, "Specific Heat", 1, print); 434 | Init::SetValues(material.rho, values[0][4], 7451.0, "Density", 1, print); 435 | 436 | Init::SetValues(material.cet_N0, values[1][0], DBL_MAX, "CET: N0", 0, print); 437 | Init::SetValues(material.cet_n, values[1][1], DBL_MAX, "CET: n", 0, print); 438 | Init::SetValues(material.cet_a, values[1][2], DBL_MAX, "CET: a", 0, print); 439 | 440 | Init::SetDiffusivity(material); 441 | 442 | return; 443 | } 444 | 445 | void Init::FileRead_Beams(vector& beams, const string& file, const bool print) { 446 | 447 | // Find location of * 448 | string s = file; 449 | string del = "*"; 450 | size_t pos = s.find(del); 451 | 452 | // If no * found, read file as is 453 | if (pos == string::npos) { 454 | Beam beam; 455 | Init::FileRead_Beam(beam, file, print); 456 | beams.push_back(beam); 457 | } 458 | // If there is a * found, start reading from ('*' = 1) 459 | else { 460 | // Find what comes before (s1) and after (s2) the '*' 461 | const string s1 = s.substr(0, pos); 462 | s.erase(0, pos + del.length()); 463 | const string s2 = s.substr(0, string::npos); 464 | 465 | checkAsterisks(s, del, file, print); 466 | 467 | // Start reading from '*' = 1 468 | uint16_t beamNum = 1; 469 | while (true) { 470 | std::ifstream readFile; 471 | readFile.exceptions(std::ifstream::failbit | std::ifstream::badbit); 472 | const string wildFile = s1 + to_string(beamNum) + s2; 473 | try { 474 | readFile.open(wildFile.c_str(), std::ios::in); 475 | } 476 | catch (const std::ifstream::failure&) { 477 | break; 478 | } 479 | Beam beam; 480 | Init::FileRead_Beam(beam, wildFile, print); 481 | beams.push_back(beam); 482 | beamNum++; 483 | } 484 | } 485 | 486 | return; 487 | } 488 | void Init::FileRead_Beam(Beam& beam, const string& file, const bool print) { 489 | vector mainWords; 490 | mainWords.push_back("Shape"); 491 | mainWords.push_back("Intensity"); 492 | 493 | vector> subWords(mainWords.size()); 494 | subWords[0].push_back("Width_X"); 495 | subWords[0].push_back("Width_Y"); 496 | subWords[0].push_back("Depth_Z"); 497 | 498 | subWords[1].push_back("Power"); 499 | subWords[1].push_back("Efficiency"); 500 | 501 | vector> values(mainWords.size()); 502 | 503 | Init::Keywords_Lv2(mainWords, subWords, values, file, print); 504 | 505 | Init::SetValues(beam.ax, values[0][0], 10.0e-6, "X Width", 1, print); 506 | Init::SetValues(beam.ay, values[0][1], 10.0e-6, "Y Width", 1, print); 507 | Init::SetValues(beam.az, values[0][2], 1.0e-6, "Z Depth", 1, print); 508 | 509 | Init::SetValues(beam.q, values[1][0], 1200, "Power", 1, print); 510 | Init::SetValues(beam.eff, values[1][1], 1.0, "Efficiency", 1, print); 511 | 512 | Init::SetBeamPower(beam); 513 | 514 | return; 515 | } 516 | 517 | void Init::FileRead_Paths(vector>& paths, const string& file, const bool print) { 518 | 519 | // Find location of * 520 | string s = file; 521 | string del = "*"; 522 | size_t pos = s.find(del); 523 | 524 | // If no * found, read file as is 525 | if (pos == string::npos) { 526 | vector path; 527 | Init::FileRead_Path(path, file, print); 528 | paths.push_back(path); 529 | } 530 | // If there is a * found, start reading from ('*' = 1) 531 | else { 532 | // Find what comes before (s1) and after (s2) the '*' 533 | const string s1 = s.substr(0, pos); 534 | s.erase(0, pos + del.length()); 535 | const string s2 = s.substr(0, string::npos); 536 | 537 | checkAsterisks(s, del, file, print); 538 | 539 | // Start reading from '*' = 1 540 | uint16_t pathNum = 1; 541 | while (true) { 542 | std::ifstream readFile; 543 | readFile.exceptions(std::ifstream::failbit | std::ifstream::badbit); 544 | const string wildFile = s1 + to_string(pathNum) + s2; 545 | try { 546 | readFile.open(wildFile.c_str(), std::ios::in); 547 | } 548 | catch (const std::ifstream::failure&) { 549 | break; 550 | } 551 | vector path; 552 | Init::FileRead_Path(path, wildFile, print); 553 | paths.push_back(path); 554 | pathNum++; 555 | } 556 | } 557 | 558 | return; 559 | } 560 | void Init::FileRead_Path(vector& path, const string& file, const bool print) { 561 | //Currently hard coding a conversion from mm to m 562 | double convert = 1e-3; 563 | 564 | std::ifstream pathfile; 565 | pathfile.exceptions(std::ifstream::failbit | std::ifstream::badbit); 566 | string line; 567 | 568 | try { 569 | pathfile.open(file.c_str(), std::ios::in); 570 | path_seg seg; 571 | 572 | //Set initial position as 0, 0, 0 573 | seg.smode = 1; 574 | seg.sx = 0.0; 575 | seg.sy = 0.0; 576 | seg.sz = 0.0; 577 | seg.sqmod = 0.0; 578 | seg.sparam = 0.0; 579 | path.push_back(seg); 580 | 581 | //Read in path information from file 582 | while (getline(pathfile, line)) 583 | { 584 | seg.smode = 1; 585 | seg.sx = 0.0; 586 | seg.sy = 0.0; 587 | seg.sz = 0.0; 588 | seg.sqmod = 0.0; 589 | seg.sparam = 0.0; 590 | 591 | pathfile >> seg.smode >> seg.sx >> seg.sy >> seg.sz >> seg.sqmod >> seg.sparam; 592 | seg.sx *= convert; 593 | seg.sy *= convert; 594 | seg.sz *= convert; 595 | path.push_back(seg); 596 | } 597 | } 598 | catch (const std::ifstream::failure&) { 599 | catchPrint(file, path.size(), print); 600 | pathfile.close(); 601 | } 602 | catch (const std::exception& e) { 603 | catchPrint(file, path.size(), print); 604 | pathfile.close(); 605 | } 606 | 607 | //Calculate path times 608 | double dt_seg, dist, dx, dy, dz; 609 | path[0].seg_time = 0; 610 | for (int seg = 1; seg < path.size(); seg++) { 611 | if (path[seg].smode) { //For spot mode 612 | path[seg].seg_time = path[seg - 1].seg_time + path[seg].sparam; 613 | } 614 | else { //For line mode 615 | dx = path[seg].sx - path[seg - 1].sx; 616 | dy = path[seg].sy - path[seg - 1].sy; 617 | dz = path[seg].sz - path[seg - 1].sz; 618 | dist = sqrt(pow(dx, 2) + pow(dy, 2) + pow(dz, 2)); 619 | dt_seg = dist / path[seg].sparam; 620 | path[seg].seg_time = path[seg - 1].seg_time + dt_seg; 621 | } 622 | } 623 | 624 | return; 625 | } 626 | 627 | void Init::FileRead_Domain(Domain& domain, const string& file, const bool print) { 628 | vector mainWords; 629 | mainWords.push_back("X"); 630 | mainWords.push_back("Y"); 631 | mainWords.push_back("Z"); 632 | mainWords.push_back("BoundaryConditions"); 633 | mainWords.push_back("Custom"); 634 | 635 | vector> subWords(mainWords.size()); 636 | subWords[0].push_back("Min"); 637 | subWords[0].push_back("Max"); 638 | subWords[0].push_back("Num"); 639 | subWords[0].push_back("Res"); 640 | 641 | subWords[1].push_back("Min"); 642 | subWords[1].push_back("Max"); 643 | subWords[1].push_back("Num"); 644 | subWords[1].push_back("Res"); 645 | 646 | subWords[2].push_back("Min"); 647 | subWords[2].push_back("Max"); 648 | subWords[2].push_back("Num"); 649 | subWords[2].push_back("Res"); 650 | 651 | subWords[3].push_back("X_min"); 652 | subWords[3].push_back("X_max"); 653 | subWords[3].push_back("Y_min"); 654 | subWords[3].push_back("Y_max"); 655 | subWords[3].push_back("Z_min"); 656 | subWords[3].push_back("Reflections"); 657 | 658 | subWords[4].push_back("File"); 659 | 660 | vector> values(mainWords.size()); 661 | 662 | Init::Keywords_Lv2(mainWords, subWords, values, file, print); 663 | 664 | Init::SetValues(domain.xmin, values[0][0], -DBL_MAX, "X min", 0, print); 665 | Init::SetValues(domain.xmax, values[0][1], DBL_MAX, "X max", 0, print); 666 | Init::SetValues(domain.xnum, values[0][2], INT_MAX, "X num", 0, print); 667 | Init::SetValues(domain.xres, values[0][3], 50e-6, "X res", 0, print); 668 | 669 | Init::SetValues(domain.ymin, values[1][0], -DBL_MAX, "Y min", 0, print); 670 | Init::SetValues(domain.ymax, values[1][1], DBL_MAX, "Y max", 0, print); 671 | Init::SetValues(domain.ynum, values[1][2], INT_MAX, "Y num", 0, print); 672 | Init::SetValues(domain.yres, values[1][3], 50e-6, "Y res", 0, print); 673 | 674 | Init::SetValues(domain.zmin, values[2][0], -DBL_MAX, "Z min", 0, print); 675 | Init::SetValues(domain.zmax, values[2][1], DBL_MAX, "Z max", 0, print); 676 | Init::SetValues(domain.znum, values[2][2], INT_MAX, "Z num", 0, print); 677 | Init::SetValues(domain.zres, values[2][3], 50e-6, "Z res", 0, print); 678 | 679 | Init::SetValues(domain.BC_xmin, values[3][0], DBL_MAX, "BC X min", 0, print); 680 | Init::SetValues(domain.BC_xmax, values[3][1], DBL_MAX, "BC X max", 0, print); 681 | Init::SetValues(domain.BC_ymin, values[3][2], DBL_MAX, "BC Y min", 0, print); 682 | Init::SetValues(domain.BC_ymax, values[3][3], DBL_MAX, "BC Y max", 0, print); 683 | Init::SetValues(domain.BC_zmin, values[3][4], DBL_MAX, "BC Z min", 0, print); 684 | Init::SetValues(domain.BC_reflections, values[3][5], INT_MAX, "BC reflections", 0, print); 685 | 686 | Init::SetValues(domain.pointsFile, values[4][0], string(""), "Point File", 0, print); 687 | 688 | // For boundary conditions 689 | if (domain.BC_xmin != DBL_MAX || domain.BC_xmax != DBL_MAX || domain.BC_ymin != DBL_MAX || domain.BC_ymax != DBL_MAX || domain.BC_zmin != DBL_MAX) { 690 | domain.use_BCs = 1; 691 | } 692 | else { 693 | domain.use_BCs = 0; 694 | } 695 | 696 | // For custom point files 697 | if (domain.pointsFile == string("")) { 698 | domain.customPoints = false; 699 | } 700 | else { 701 | domain.customPoints = true; 702 | FileRead_Points(domain, domain.pointsFile, print); 703 | } 704 | 705 | 706 | return; 707 | } 708 | 709 | void Init::FileRead_Output(Output& output, const string& file, const bool print) { 710 | vector mainWords; 711 | mainWords.push_back("Grid"); 712 | mainWords.push_back("Temperature"); 713 | mainWords.push_back("Solidification"); 714 | mainWords.push_back("Solidification+"); 715 | 716 | vector> subWords(mainWords.size()); 717 | subWords[0].push_back("x"); 718 | subWords[0].push_back("y"); 719 | subWords[0].push_back("z"); 720 | 721 | subWords[1].push_back("T"); 722 | subWords[1].push_back("T_hist"); 723 | 724 | subWords[2].push_back("tSol"); 725 | subWords[2].push_back("G"); 726 | subWords[2].push_back("Gx"); 727 | subWords[2].push_back("Gy"); 728 | subWords[2].push_back("Gz"); 729 | subWords[2].push_back("V"); 730 | subWords[2].push_back("dTdt"); 731 | subWords[2].push_back("eqFrac"); 732 | subWords[2].push_back("depth"); 733 | subWords[2].push_back("numMelt"); 734 | subWords[2].push_back("RDF"); 735 | subWords[2].push_back("MP_Stats"); 736 | 737 | subWords[3].push_back("H"); 738 | subWords[3].push_back("Hx"); 739 | subWords[3].push_back("Hy"); 740 | subWords[3].push_back("Hz"); 741 | 742 | vector> values(mainWords.size()); 743 | 744 | Init::Keywords_Lv2(mainWords, subWords, values, file, print); 745 | 746 | Init::SetValues(output.x, values[0][0], true, "output-x", 1, print); 747 | Init::SetValues(output.y, values[0][1], true, "output-y", 1, print); 748 | Init::SetValues(output.z, values[0][2], true, "output-z", 1, print); 749 | 750 | Init::SetValues(output.T, values[1][0], true, "output-T", 1, print); 751 | Init::SetValues(output.T_hist, values[1][1], false, "output-T_hist", 1, print); 752 | 753 | Init::SetValues(output.tSol, values[2][0], false, "output-tSol", 1, print); 754 | Init::SetValues(output.G, values[2][1], false, "output-G", 1, print); 755 | Init::SetValues(output.Gx, values[2][2], false, "output-Gx", 1, print); 756 | Init::SetValues(output.Gy, values[2][3], false, "output-Gy", 1, print); 757 | Init::SetValues(output.Gz, values[2][4], false, "output-Gz", 1, print); 758 | Init::SetValues(output.V, values[2][5], false, "output-V", 1, print); 759 | Init::SetValues(output.dTdt, values[2][6], false, "output-dTdt", 1, print); 760 | Init::SetValues(output.eqFrac, values[2][7], false, "output-eqFrac", 1, print); 761 | Init::SetValues(output.depth, values[2][8], false, "output-depth", 1, print); 762 | Init::SetValues(output.numMelt, values[2][9], false, "output-numMelt", 1, print); 763 | Init::SetValues(output.RDF, values[2][10], false, "output-RDF", 1, print); 764 | Init::SetValues(output.mp_stats, values[2][11], false, "output-mpStats", 1, print); 765 | 766 | Init::SetValues(output.H, values[3][0], false, "output-H", 1, print); 767 | Init::SetValues(output.Hx, values[3][1], false, "output-Hx", 1, print); 768 | Init::SetValues(output.Hy, values[3][2], false, "output-Hy", 1, print); 769 | Init::SetValues(output.Hz, values[3][3], false, "output-Hz", 1, print); 770 | return; 771 | } 772 | void Init::FileRead_Settings(Settings& settings, const string& file, const bool print) { 773 | 774 | vector mainWords; 775 | 776 | mainWords.push_back("Temperature"); 777 | mainWords.push_back("Path"); 778 | mainWords.push_back("Compute"); 779 | mainWords.push_back("MPI"); 780 | 781 | vector> subWords(mainWords.size()); 782 | 783 | subWords[0].push_back("Sol_Tol"); 784 | subWords[0].push_back("Sol_Iter"); 785 | subWords[0].push_back("Cutoff_Peak"); 786 | subWords[0].push_back("Cutoff_T0TL"); 787 | 788 | subWords[1].push_back("Buffer"); 789 | subWords[1].push_back("Compression"); 790 | 791 | subWords[2].push_back("MaxThreads"); 792 | subWords[2].push_back("PINT"); 793 | 794 | subWords[3].push_back("Overlap"); 795 | 796 | vector> values(mainWords.size()); 797 | 798 | Init::Keywords_Lv2(mainWords, subWords, values, file, print); 799 | 800 | Init::SetValues(settings.dttest, values[0][0], 1e-3, "Solidification Tolerance", 0, print); 801 | Init::SetValues(settings.max_iter, values[0][1], 10, "Solidification Iterations", 0, print); 802 | Init::SetValues(settings.t_hist, values[0][2], 1e-9, "Peak Temperature Cutoff Ratio", 0, print); 803 | Init::SetValues(settings.p_hist, values[0][3], 1e-2, "Solidfication to Initial Temperature Cutoff Ratio", 0, print); 804 | 805 | Init::SetValues(settings.r_max, values[1][0], -1.0, "Domain Buffer", 0, print); 806 | Init::SetValues(settings.compress, values[1][1], 0, "Path Compression", 0, print); 807 | 808 | Init::SetValues(settings.thnum, values[2][0], omp_get_max_threads()/2, "Number of Threads", 0, print); 809 | Init::SetValues(settings.use_PINT, values[2][1], 0, "Parallel in Time Mode", 0, print); 810 | 811 | Init::SetValues(settings.mpi_overlap, values[3][0], 0, "Mpi Overlap", 0, print); 812 | 813 | return; 814 | } 815 | 816 | void Init::FileRead_Points(Domain& domain, const string& file, const bool print) { 817 | std::ifstream readFile; 818 | readFile.exceptions(std::ifstream::failbit | std::ifstream::badbit); 819 | string line; 820 | int num_read = 0; 821 | try { 822 | readFile.open(file.c_str(), std::ios::in); 823 | coord temp; 824 | 825 | //Set initial position as 0, 0, 0 826 | temp.x = 0.0; 827 | temp.y = 0.0; 828 | temp.z = 0.0; 829 | 830 | //Read in path information from file 831 | while (getline(readFile, line)) 832 | { 833 | readFile >> temp.x >> temp.y >> temp.z; 834 | temp.x /= 1000.0; temp.y /= 1000.0; temp.z /= 1000.0; 835 | domain.points.push_back(temp); 836 | num_read++; 837 | } 838 | } 839 | catch (const std::ifstream::failure&) { 840 | catchPrint(file, num_read, print); 841 | readFile.close(); 842 | } 843 | catch (const std::exception& e) { 844 | catchPrint(file, num_read, print); 845 | readFile.close(); 846 | } 847 | 848 | domain.pnum = num_read; 849 | if (num_read) { 850 | if (print) std::cout << domain.pnum << " points found in " << file << std::endl; 851 | } 852 | else { 853 | if (print) std::cout << "ERROR: No points found in " << file << std::endl; 854 | } 855 | 856 | return; 857 | } 858 | 859 | void Init::SetDiffusivity(Material& material) { 860 | material.a = material.kon / (material.rho * material.cps); 861 | } 862 | void Init::SetBeamPower(Beam& beam){ 863 | if (beam.eff == DBL_MAX) { beam.eff = 1.0; } 864 | beam.q = beam.q * beam.eff * 2.0; 865 | } 866 | void Init::SetDomainParams(Domain& domain) { 867 | 868 | // If using a custom point file, skip 869 | if (domain.customPoints) { return; } 870 | 871 | // Otherwise, set (x,y,z) parameters (num, res, etc.) 872 | if (domain.xnum == INT_MAX) { 873 | domain.xnum = 1 + int(0.5 + (domain.xmax - domain.xmin) / domain.xres); 874 | domain.xmax = domain.xmin + (domain.xnum - 1) * domain.xres; 875 | } 876 | else if (domain.xnum != 1) { 877 | domain.xres = (domain.xmax - domain.xmin) / (domain.xnum - 1); 878 | } 879 | else { 880 | domain.xres = DBL_MAX; 881 | } 882 | 883 | if (domain.ynum == INT_MAX) { 884 | domain.ynum = 1 + int(0.5 + (domain.ymax -domain.ymin) / domain.yres); 885 | domain.ymax = domain.ymin + (domain.ynum - 1) * domain.yres; 886 | } 887 | else if (domain.ynum != 1) { 888 | domain.yres = (domain.ymax - domain.ymin) / (domain.ynum - 1); 889 | } 890 | else { 891 | domain.yres = DBL_MAX; 892 | } 893 | 894 | if (domain.znum == INT_MAX) { 895 | domain.znum = 1 + int(0.5 + (domain.zmax - domain.zmin) / domain.zres); 896 | domain.zmax = domain.zmin + (domain.znum - 1) * domain.zres; 897 | } 898 | else if (domain.ynum != 1) { 899 | domain.zres = (domain.zmax - domain.zmin) / (domain.znum - 1); 900 | } 901 | else { 902 | domain.zres = DBL_MAX; 903 | } 904 | 905 | 906 | 907 | domain.pnum = domain.xnum * domain.ynum * domain.znum; 908 | } 909 | --------------------------------------------------------------------------------