Skip to content

Commit

Permalink
Merge pull request #245 from kuanchihwang/feature-mpas-build-infra
Browse files Browse the repository at this point in the history
Integrate MPAS build infrastructure with CIME
  • Loading branch information
kuanchihwang authored Jan 25, 2024
2 parents aaeac7f + 3a9b8ad commit bf7302c
Show file tree
Hide file tree
Showing 4 changed files with 183 additions and 5 deletions.
49 changes: 49 additions & 0 deletions cime_config/buildlib
Original file line number Diff line number Diff line change
Expand Up @@ -141,6 +141,11 @@ def _build_cam():
#-------------------------------------------------------
# build the library
#-------------------------------------------------------

# If dynamical core is MPAS, setup its build infrastructure so it can be built along with CAM below.
if dycore == "mpas":
_setup_mpas(case)

complib = os.path.join(libroot, "libatm.a")
makefile = os.path.join(casetools, "Makefile")

Expand All @@ -156,6 +161,50 @@ def _build_cam():
_LOGGER.info("%s: \n\n output:\n %s \n\n err:\n\n%s\n", cmd, out, err)
expect(retcode == 0, f"Command {cmd} failed with rc={retcode}")

def _setup_mpas(case: Case) -> None:
"""
Setup MPAS build infrastructure.
"""

atm_src_root = os.path.normpath(case.get_value("COMP_ROOT_DIR_ATM"))
atm_bld_root = os.path.normpath(os.path.join(case.get_value("EXEROOT"), "atm", "obj"))

mpas_dycore_src_root = os.path.join(atm_src_root, "src", "dynamics", "mpas", "dycore", "src")
mpas_dycore_bld_root = os.path.join(atm_bld_root, "mpas")

# Make sure `mpas_dycore_src_root` exists. If not, it is likely that `./manage_externals/checkout_externals` did not succeed.
if os.path.isfile(mpas_dycore_src_root):
raise FileExistsError(1, "Unexpected file", mpas_dycore_src_root)

if not os.path.isdir(mpas_dycore_src_root):
raise FileNotFoundError(1, "No such directory", mpas_dycore_src_root)

# MPAS supports "in-source" build only. Copy source code to `mpas_dycore_bld_root` to avoid polluting `mpas_dycore_src_root`.
if os.path.isfile(mpas_dycore_bld_root):
raise FileExistsError(1, "Unexpected file", mpas_dycore_bld_root)

shutil.copytree(mpas_dycore_src_root, mpas_dycore_bld_root, copy_function=_copy2_as_needed, dirs_exist_ok=True)
shutil.move(os.path.join(mpas_dycore_bld_root, "Makefile"), os.path.join(mpas_dycore_bld_root, "Makefile.CESM"))
_copy2_as_needed(os.path.normpath(os.path.join(mpas_dycore_src_root, os.pardir, os.pardir, "Makefile")), os.path.join(mpas_dycore_bld_root, "Makefile"))
_copy2_as_needed(os.path.normpath(os.path.join(mpas_dycore_src_root, os.pardir, os.pardir, "Makefile.in.CESM")), os.path.join(mpas_dycore_bld_root, "Makefile.in.CESM"))

def _copy2_as_needed(src: str, dst: str) -> None:
"""
Wrapper around `shutil.copy2`. Copy the file `src` to the file or directory `dst` as needed.
"""

if os.path.isfile(src):
if os.path.isfile(dst):
if int(os.path.getmtime(src)) != int(os.path.getmtime(dst)) or os.path.getsize(src) != os.path.getsize(dst):
# `src` and `dst` are both files but their modification time or size differ.
# Example scenario: User modified some existing source code files.
shutil.copy2(src, dst)
else:
if os.path.isdir(dst) or os.path.isdir(os.path.dirname(dst)):
# `src` is a new file that does not exist at `dst`.
# Example scenario: User added some new source code files.
shutil.copy2(src, dst)

###############################################################################

if __name__ == "__main__":
Expand Down
12 changes: 7 additions & 5 deletions cime_config/config_component.xml
Original file line number Diff line number Diff line change
Expand Up @@ -103,10 +103,11 @@

<entry id="CAM_DYCORE">
<type>char</type>
<valid_values>eul,fv,se,none</valid_values>
<valid_values>eul,fv,mpas,se,none</valid_values>
<default_value>fv</default_value>
<values match="last">
<value grid="a%T[1-9]" >eul</value>
<value grid="a%T[1-9]">eul</value>
<value grid="a%mpasa[0-9]+">mpas</value>
<value grid="a%ne[0-9]">se</value>
</values>
<group>build_component_cam</group>
Expand Down Expand Up @@ -305,9 +306,10 @@
<type>char</type>
<valid_values></valid_values>
<default_value>-lmusica -ljsonfortran</default_value>
<values match="last">
<!--Turn off for physics testbed -->
<value compset="_CAM%PHYSTEST"> </value>
<values match="last" modifier="additive">
<!-- Turn off for physics testbed -->
<value compset="_CAM%PHYSTEST"></value>
<value grid="a%mpasa[0-9]+">-lmpas</value>
</values>
<group>build_component_cam</group>
<file>env_build.xml</file>
Expand Down
9 changes: 9 additions & 0 deletions src/dynamics/mpas/Makefile
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
# This Makefile is invoked by CIME Makefile (i.e., `cime/CIME/Tools/Makefile`).

# Some targets in MPAS build infrastructure are sensitive to this environment variable. Override it to avoid build issues.
override PWD = $(CURDIR)
export PWD

all:
$(MAKE) -f Makefile.CESM libmpas-prepare ESM="CESM"
$(MAKE) -f Makefile.CESM libmpas-build ESM="CESM"
118 changes: 118 additions & 0 deletions src/dynamics/mpas/Makefile.in.CESM
Original file line number Diff line number Diff line change
@@ -0,0 +1,118 @@
ifeq ($(strip $(LIBROOT)),)
$(warning `LIBROOT` should not be empty. Defaulting to `..`)

LIBROOT = ..
endif

#
# Define and export variables used by MPAS build infrastructure.
#

export CP = cp -afv
export MKDIR = mkdir -pv
export RM = rm -frv

# Constants.
export AUTOCLEAN = false
export BUILD_TARGET = N/A
export CORE = atmosphere
export EXE_NAME = atmosphere_model
export GEN_F90 = false
export GIT_VERSION = N/A
export NAMELIST_SUFFIX = atmosphere

# Customize variables (e.g., build options) for use with CESM.
export AR := ar
export ARFLAGS := -M
export CPP := cpp -P -traditional
export CPPFLAGS := -D_MPI \
-DMPAS_BUILD_TARGET="$(BUILD_TARGET)" \
-DMPAS_CAM_DYCORE \
-DMPAS_EXE_NAME="$(EXE_NAME)" \
-DMPAS_EXTERNAL_ESMF_LIB \
-DMPAS_GIT_VERSION="$(GIT_VERSION)" \
-DMPAS_NAMELIST_SUFFIX="$(NAMELIST_SUFFIX)" \
-DMPAS_NATIVE_TIMERS \
-DMPAS_NO_ESMF_INIT \
-DMPAS_PIO_SUPPORT
# `PIODEF` is defined by CIME Makefile (i.e., `cime/CIME/Tools/Makefile`). Its value can be empty or `-DUSE_PIO2`.
ifneq ($(strip $(PIODEF)),)
export CPPFLAGS += $(strip $(PIODEF))
endif
export LINKER := $(strip $(FC))
export SCC := $(strip $(CC))
export SCXX := $(strip $(CXX))
export SFC := $(strip $(FC))

#
# Targets.
#

.PHONY: all
all:
@echo 'Supplemental Makefile for MPAS Dynamical Core in CESM'
@echo ''
@echo 'MPAS will be built as a static library located at `$${LIBROOT}/libmpas.a`.'
@echo 'Users are responsible to provide all necessary build options via environment variables or command line arguments.'
@echo ''
@echo 'Usage hints:'
@echo ' `make libmpas-prepare ESM="CESM" LIBROOT="..."`'
@echo ' `make libmpas-build ESM="CESM" LIBROOT="..."`'
@echo ' `make libmpas-clean ESM="CESM" LIBROOT="..."`'

.PHONY: libmpas-prepare
libmpas-prepare: libmpas-archiver-script.txt libmpas-no-physics libmpas-preview

# Combine multiple static libraries into `libmpas.a` via archiver/MRI script. This requires GNU or GNU-like archiver (`ar`) program.
libmpas-archiver-script.txt:
@echo "create libmpas.a" > $(@)
@echo "addlib libdycore.a" >> $(@)
@echo "addlib libframework.a" >> $(@)
@echo "addlib libops.a" >> $(@)
@echo "save" >> $(@)
@echo "end" >> $(@)

# Do not use built-in MPAS/WRF physics.
.PHONY: libmpas-no-physics
libmpas-no-physics:
@sed -E -i -e "s/^ *PHYSICS=.+$$/PHYSICS=/g" core_atmosphere/Makefile

.PHONY: libmpas-preview
libmpas-preview:
@echo "Previewing build options for $(LIBROOT)/libmpas.a:"
@echo "AR = $(AR)"
@echo "ARFLAGS = $(ARFLAGS)"
@echo "CC = $(CC)"
@echo "CFLAGS = $(CFLAGS)"
@echo "CPP = $(CPP)"
@echo "CPPFLAGS = $(CPPFLAGS)"
@echo "CPPINCLUDES = $(CPPINCLUDES)"
@echo "CXX = $(CXX)"
@echo "CXXFLAGS = $(CXXFLAGS)"
@echo "FC = $(FC)"
@echo "FCINCLUDES = $(FCINCLUDES)"
@echo "FFLAGS = $(FFLAGS)"
@echo "LDFLAGS = $(LDFLAGS)"
@echo "LIBS = $(LIBS)"
@echo "LINKER = $(LINKER)"
@echo "SCC = $(SCC)"
@echo "SCXX = $(SCXX)"
@echo "SFC = $(SFC)"

.PHONY: libmpas-build
libmpas-build: $(LIBROOT)/libmpas.a

$(LIBROOT)/libmpas.a: libmpas.a
$(MKDIR) $(LIBROOT)
$(CP) $(<) $(@)

libmpas.a: $(AUTOCLEAN_DEPS) dycore externals frame ops
$(AR) $(ARFLAGS) < libmpas-archiver-script.txt

.PHONY: libmpas-clean
libmpas-clean: clean
$(RM) $(LIBROOT)/libmpas.a libmpas.a

.PHONY: externals
externals: $(AUTOCLEAN_DEPS)
( cd external; $(MAKE) FC="$(FC)" SFC="$(SFC)" CC="$(CC)" SCC="$(SCC)" FFLAGS="$(FFLAGS)" CFLAGS="$(CFLAGS)" CPP="$(CPP)" NETCDF="$(NETCDF)" CORE="$(CORE)" ezxml-lib )

0 comments on commit bf7302c

Please sign in to comment.