Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Case Optimization Refresh #248

Merged
merged 1 commit into from
Dec 12, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
19 changes: 12 additions & 7 deletions CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -229,6 +229,8 @@ macro(HANDLE_SOURCES target useCommon)
set(${target}_DIR "${CMAKE_SOURCE_DIR}/src/${target}")
set(common_DIR "${CMAKE_SOURCE_DIR}/src/common")

string(TOUPPER ${target} ${target}_UPPER)

# Gather src/[<target>,common]/*.f90
file(GLOB ${target}_F90s CONFIGURE_DEPENDS "${${target}_DIR}/*.f90")
set(${target}_SRCs ${${target}_F90s})
Expand All @@ -245,9 +247,9 @@ macro(HANDLE_SOURCES target useCommon)
endif()

# Locate src/[<target>,common]/include/*.fpp
if (EXISTS "${${target}_DIR}/include")
file(GLOB ${target}_incs CONFIGURE_DEPENDS "${${target}_DIR}/include/*.fpp")
endif()
file(GLOB ${target}_incs CONFIGURE_DEPENDS "${${target}_DIR}/include/*.fpp"
"${CMAKE_CURRENT_BINARY_DIR}/include/*.fpp")

if (${useCommon})
file(GLOB common_incs CONFIGURE_DEPENDS "${common_DIR}/include/*.fpp")
list(APPEND ${target}_incs ${common_incs})
Expand All @@ -261,11 +263,14 @@ macro(HANDLE_SOURCES target useCommon)
add_custom_command(
OUTPUT ${f90}
COMMAND ${FYPP_EXE} -m re
-I "${common_DIR}"
-I "${common_DIR}/include"
-I "${CMAKE_CURRENT_BINARY_DIR}/include"
-I "${${target}_DIR}/include"
-I "${common_DIR}/include"
-I "${common_DIR}"
-D MFC_${CMAKE_Fortran_COMPILER_ID}
-D MFC_${${target}_UPPER}
-D MFC_COMPILER="${CMAKE_Fortran_COMPILER_ID}"
-D MFC_CASE_OPTIMIZATION=False
--line-numbering
--no-folding
"${fpp}" "${f90}"
Expand Down Expand Up @@ -317,10 +322,10 @@ function(MFC_SETUP_TARGET)
"${CMAKE_SOURCE_DIR}/src/${ARGS_TARGET}/include")
endif()

string(TOUPPER "${ARGS_TARGET}" ARGS_TARGET_UPPER)
string(TOUPPER "${ARGS_TARGET}" ${ARGS_TARGET}_UPPER)
target_compile_definitions(
${ARGS_TARGET} PRIVATE MFC_${CMAKE_Fortran_COMPILER_ID}
MFC_${ARGS_TARGET_UPPER}
MFC_${${ARGS_TARGET}_UPPER}
)

if (MFC_MPI AND ARGS_MPI)
Expand Down
5 changes: 1 addition & 4 deletions src/pre_process/include/case.fpp
Original file line number Diff line number Diff line change
@@ -1,7 +1,4 @@
! This file was generated by MFC. It is only used when analytical patches are
! present in the input file. It is used to define the analytical patches with
! expressions that are evaluated at runtime from the input file.
! By default, no analytical patches are defined.

#:def analytical()

#:enddef
7 changes: 2 additions & 5 deletions src/simulation/include/case.fpp
Original file line number Diff line number Diff line change
@@ -1,5 +1,2 @@
! This file was generated by MFC. It is only used if the --case-optimization
! option is passed to ./mfc.sh run or test, enabling a GPU-oriented optimization
! that hard-codes certain case parameters from the input file.

#:set MFC_CASE_OPTIMIZATION = False
! This file is purposefully empty. It is only important for builds that make use
! of --case-optimization.
35 changes: 22 additions & 13 deletions toolchain/mfc/args.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import re, os.path, argparse, dataclasses

from .build import TARGETS, DEFAULT_TARGETS, DEPENDENCY_TARGETS
from .common import format_list_to_string
from .common import MFCException, format_list_to_string
from .test.test import CASES as TEST_CASES
from .packer import packer

Expand Down Expand Up @@ -45,7 +45,7 @@ def add_common_arguments(p, mask = None):

if "t" not in mask:
p.add_argument("-t", "--targets", metavar="TARGET", nargs="+", type=str.lower, choices=[ _.name for _ in TARGETS ],
default=[ _.name for _ in DEFAULT_TARGETS ],
default=[ _.name for _ in sorted(DEFAULT_TARGETS, key=lambda t: t.runOrder) ],
help=f"Space separated list of targets to act upon. Allowed values are: {format_list_to_string([ _.name for _ in TARGETS ])}.")

if "m" not in mask:
Expand All @@ -70,6 +70,8 @@ def add_common_arguments(p, mask = None):

# === BUILD ===
add_common_arguments(build, "g")
build.add_argument("-i", "--input", type=str, default=None, help="(GPU Optimization) Build a version of MFC optimized for a case.")
build.add_argument("--case-optimization", action="store_true", default=False, help="(GPU Optimization) Compile MFC targets with some case parameters hard-coded (requires --input).")

# === CLEAN ===
add_common_arguments(clean, "jg")
Expand Down Expand Up @@ -128,16 +130,17 @@ def add_common_arguments(p, mask = None):
args: dict = vars(parser.parse_args())

# Add default arguments of other subparsers
def append_defaults_to_data(name: str, parser):
if args["command"] != name:
vals, errs = parser.parse_known_args(["-i None"])
for key,val in vars(vals).items():
if not key in args:
args[key] = val
for name, parser in [("run", run), ("test", test), ("build", build),
("clean", clean), ("bench", bench), ("count", count)]:
if args["command"] == name:
continue

for a, b in [("run", run ), ("test", test ), ("build", build),
("clean", clean), ("bench", bench), ("count", count)]:
append_defaults_to_data(a, b)
vals, errs = parser.parse_known_args(["-i", "None"])
for key, val in vars(vals).items():
if key == "input":
args[key] = args.get(key)
elif not key in args:
args[key] = args.get(key, val)

if args["command"] is None:
parser.print_help()
Expand All @@ -146,11 +149,17 @@ def append_defaults_to_data(name: str, parser):
# "Slugify" the name of the job
args["name"] = re.sub(r'[\W_]+', '-', args["name"])

# build's --case-optimization and --input depend on each other
if args["command"] == "build":
if (args["input"] is not None) ^ args["case_optimization"] :
raise MFCException(f"./mfc.sh build's --case-optimization requires --input")

# Input files to absolute paths
for e in ["input", "input1", "input2"]:
if e not in args:
continue
if args[e] is not None:

if args.get(e) is not None:
args[e] = os.path.abspath(args[e])

return args
151 changes: 86 additions & 65 deletions toolchain/mfc/build.py
Original file line number Diff line number Diff line change
@@ -1,10 +1,9 @@
import re, os, typing, dataclasses
import re, os, typing, hashlib, dataclasses

from .common import MFCException, system, delete_directory, create_directory
from .state import ARG, CFG
from .printer import cons
from .run.input import MFCInputFile

from .run import input

@dataclasses.dataclass
class MFCTarget:
Expand All @@ -26,19 +25,27 @@ def compute(self) -> typing.Set:
isDefault: bool # Should it be built by default? (unspecified -t | --targets)
isRequired: bool # Should it always be built? (no matter what -t | --targets is)
requires: Dependencies # Build dependencies of the target
runOrder: int # For MFC Targets: Order in which targets should logically run

def __hash__(self) -> int:
return hash(self.name)

# Get path to directory that will store the build files
def get_build_dirpath(self) -> str:
subdir = 'dependencies' if self.isDependency else CFG().make_slug()

if not self.isDependency and ARG("case_optimization"):
m = hashlib.sha256()
m.update(input.load().get_fpp(self).encode())
subdir = f"{subdir}-{m.hexdigest()[:6]}"

return os.sep.join([
os.getcwd(),
"build",
[CFG().make_slug(), 'dependencies'][int(self.isDependency)],
subdir,
self.name
])

# Get the directory that contains the target's CMakeLists.txt
def get_cmake_dirpath(self) -> str:
# The CMakeLists.txt file is located:
Expand Down Expand Up @@ -84,31 +91,16 @@ def get_configuration_txt(self) -> typing.Optional[dict]:

return None


def build(self, history: typing.Set[str] = None):
if history is None:
history = set()

if self.name in history:
return

history.add(self.name)

build_targets(REQUIRED_TARGETS, history)

cons.print(f"[bold]Building [magenta]{self.name}[/magenta]:[/bold]")
cons.indent()

def is_buildable(self) -> bool:
if ARG("no_build"):
cons.print("--no-build specified, skipping...")
cons.unindent()
return
return False

if self.isDependency and ARG(f"no_{self.name}"):
cons.print(f"--no-{self.name} given, skipping...")
cons.unindent()
return
return False

return True

def configure(self):
build_dirpath = self.get_build_dirpath()
cmake_dirpath = self.get_cmake_dirpath()
install_dirpath = self.get_install_dirpath()
Expand Down Expand Up @@ -144,42 +136,35 @@ def build(self, history: typing.Set[str] = None):
flags.append(f"-DMFC_OpenACC={'ON' if ARG('gpu') else 'OFF'}")

configure = ["cmake"] + flags + ["-S", cmake_dirpath, "-B", build_dirpath]
build = ["cmake", "--build", build_dirpath,
"--target", self.name,
"-j", ARG("jobs"),
"--config", 'Debug' if ARG('debug') else 'Release']
if ARG('verbose'):
build.append("--verbose")

install = ["cmake", "--install", build_dirpath]
delete_directory(build_dirpath)
create_directory(build_dirpath)

if not self.is_configured():
build_targets(self.requires.compute(), history)
if system(configure, no_exception=True) != 0:
raise MFCException(f"Failed to configure the [bold magenta]{self.name}[/bold magenta] target.")


def build(self):
input.load({}).generate_fpp(self)

delete_directory(build_dirpath)
create_directory(build_dirpath)
build = ["cmake", "--build", self.get_build_dirpath(),
"--target", self.name,
"-j", ARG("jobs"),
"--config", 'Debug' if ARG('debug') else 'Release']
if ARG('verbose'):
build.append("--verbose")

if system(configure, no_exception=True) != 0:
raise MFCException(f"Failed to configure the [bold magenta]{self.name}[/bold magenta] target.")
system(build, exception_text=f"Failed to build the [bold magenta]{self.name}[/bold magenta] target.")

if not self.isDependency and ARG("command") == "build":
MFCInputFile("", "", {}).generate(self, bOnlyFPPs = True)
def install(self):
install = ["cmake", "--install", self.get_build_dirpath()]

system(build, exception_text=f"Failed to build the [bold magenta]{self.name}[/bold magenta] target.")
system(install, exception_text=f"Failed to install the [bold magenta]{self.name}[/bold magenta] target.")

cons.print(no_indent=True)
cons.unindent()

def clean(self):
cons.print(f"[bold]Cleaning [magenta]{self.name}[/magenta]:[/bold]")
cons.indent()

build_dirpath = self.get_build_dirpath()

if not os.path.isdir(build_dirpath):
cons.print("Target not configured. Nothing to clean.")
cons.unindent()
return

clean = ["cmake", "--build", build_dirpath, "--target", "clean",
Expand All @@ -190,17 +175,15 @@ def clean(self):

system(clean, exception_text=f"Failed to clean the [bold magenta]{self.name}[/bold magenta] target.")

cons.unindent()


FFTW = MFCTarget('fftw', ['-DMFC_FFTW=ON'], True, False, False, MFCTarget.Dependencies([], [], []))
HDF5 = MFCTarget('hdf5', ['-DMFC_HDF5=ON'], True, False, False, MFCTarget.Dependencies([], [], []))
SILO = MFCTarget('silo', ['-DMFC_SILO=ON'], True, False, False, MFCTarget.Dependencies([HDF5], [], []))
PRE_PROCESS = MFCTarget('pre_process', ['-DMFC_PRE_PROCESS=ON'], False, True, False, MFCTarget.Dependencies([], [], []))
SIMULATION = MFCTarget('simulation', ['-DMFC_SIMULATION=ON'], False, True, False, MFCTarget.Dependencies([], [FFTW], []))
POST_PROCESS = MFCTarget('post_process', ['-DMFC_POST_PROCESS=ON'], False, True, False, MFCTarget.Dependencies([FFTW, SILO], [], []))
SYSCHECK = MFCTarget('syscheck', ['-DMFC_SYSCHECK=ON'], False, False, True, MFCTarget.Dependencies([], [], []))
DOCUMENTATION = MFCTarget('documentation', ['-DMFC_DOCUMENTATION=ON'], False, False, False, MFCTarget.Dependencies([], [], []))
FFTW = MFCTarget('fftw', ['-DMFC_FFTW=ON'], True, False, False, MFCTarget.Dependencies([], [], []), -1)
HDF5 = MFCTarget('hdf5', ['-DMFC_HDF5=ON'], True, False, False, MFCTarget.Dependencies([], [], []), -1)
SILO = MFCTarget('silo', ['-DMFC_SILO=ON'], True, False, False, MFCTarget.Dependencies([HDF5], [], []), -1)
PRE_PROCESS = MFCTarget('pre_process', ['-DMFC_PRE_PROCESS=ON'], False, True, False, MFCTarget.Dependencies([], [], []), 0)
SIMULATION = MFCTarget('simulation', ['-DMFC_SIMULATION=ON'], False, True, False, MFCTarget.Dependencies([], [FFTW], []), 1)
POST_PROCESS = MFCTarget('post_process', ['-DMFC_POST_PROCESS=ON'], False, True, False, MFCTarget.Dependencies([FFTW, SILO], [], []), 2)
SYSCHECK = MFCTarget('syscheck', ['-DMFC_SYSCHECK=ON'], False, False, True, MFCTarget.Dependencies([], [], []), -1)
DOCUMENTATION = MFCTarget('documentation', ['-DMFC_DOCUMENTATION=ON'], False, False, False, MFCTarget.Dependencies([], [], []), -1)

TARGETS = { FFTW, HDF5, SILO, PRE_PROCESS, SIMULATION, POST_PROCESS, SYSCHECK, DOCUMENTATION }

Expand Down Expand Up @@ -230,17 +213,55 @@ def get_dependency_install_dirpath() -> str:
raise MFCException("No dependency target found.")


def build_target(target: typing.Union[MFCTarget, str], history: typing.Set[str] = None):
if history is None:
history = set()

t = get_target(target)

if t.name in history or not t.is_buildable():
return

history.add(t.name)

build_targets(t.requires.compute(), history)

if not t.is_configured():
t.configure()

t.build()
t.install()

def build_targets(targets: typing.Iterable[typing.Union[MFCTarget, str]], history: typing.Set[str] = None):
if history is None:
history = set()

for target in list(REQUIRED_TARGETS) + targets:
build_target(target, history)


def clean_target(target: typing.Union[MFCTarget, str], history: typing.Set[str] = None):
if history is None:
history = set()

t = get_target(target)

for target in targets:
get_target(target).build(history)
if t.name in history or not t.is_buildable():
return

history.add(t.name)

t.clean()


def clean_targets(targets: typing.Iterable[typing.Union[MFCTarget, str]], history: typing.Set[str] = None):
if history is None:
history = set()

def clean_targets(targets: typing.Iterable[typing.Union[MFCTarget, str]]):
for target in targets:
get_target(target).clean()
for target in list(REQUIRED_TARGETS) + targets:
t = get_target(target)
if t.is_configured():
t.clean()


def get_configured_targets() -> typing.List[MFCTarget]:
Expand Down
1 change: 1 addition & 0 deletions toolchain/mfc/run/engines.py
Original file line number Diff line number Diff line change
Expand Up @@ -161,6 +161,7 @@ def run(self, targets: typing.List[MFCTarget]) -> None:

cons.print(f"[bold green]Done[/bold green] (in {datetime.timedelta(seconds=end_time - start_time)})")

cons.print()
cons.unindent()


Expand Down
Loading
Loading