Skip to content

Commit 693a077

Browse files
committed
merge dev
2 parents 3eb5e83 + 91414fd commit 693a077

File tree

8 files changed

+146
-86
lines changed

8 files changed

+146
-86
lines changed

cime_config/atm_musica_config.py

+3-2
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,10 @@
11
"""
22
The URLs and tags provided in this script are read by buildlib to build
3-
and install the MUSICA library, as well as to download the MUSICA configuration."
3+
and install the MUSICA library, as well as to download the MUSICA configuration.
44
"""
55

6+
MUSICA_CCPP_SCHEME_NAME = "musica_ccpp"
67
MUSICA_REPO_URL = "https://github.com/NCAR/musica.git"
78
MUSICA_TAG = "cc39bb00d2220fc81c85b22d3ceea4a39bd2bacf"
89
CHEMISTRY_DATA_REPO_URL = "https://github.com/NCAR/cam-sima-chemistry-data.git"
9-
CHEMISTRY_DATA_TAG = "b81cbc2e61c41ecd2d09167d6736daf1bf1be149"
10+
CHEMISTRY_DATA_TAG = "2b58f2410ec7a565bcf80dee16ec20f6bc35d78b"

cime_config/buildlib

+129-75
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ import subprocess
1212
import logging
1313

1414
from cam_config import ConfigCAM # CAM's configure structure
15+
from atm_musica_config import MUSICA_CCPP_SCHEME_NAME
1516
from atm_musica_config import MUSICA_REPO_URL, MUSICA_TAG
1617
from atm_musica_config import CHEMISTRY_DATA_REPO_URL, CHEMISTRY_DATA_TAG
1718

@@ -25,11 +26,12 @@ sys.path.append(os.path.join(__CIMEROOT, "CIME", "Tools"))
2526
#pylint: disable=wrong-import-position
2627
# CIME imports
2728
from CIME.case import Case
28-
from CIME.utils import run_cmd, expect, symlink_force
29+
from CIME.utils import run_cmd, expect
2930
from CIME.utils import stop_buffering_output
3031
from CIME.buildlib import parse_input
3132
from CIME.build import get_standard_makefile_args
3233
from CIME.Tools.standard_script_setup import check_minimum_python_version
34+
from CIME.locked_files import lock_file, unlock_file
3335
#pylint: enable=wrong-import-position
3436

3537
check_minimum_python_version(3, 7) #CAM requires version 3.7 or greater
@@ -76,10 +78,9 @@ def _build_cam():
7678
# Re-run source generator in case registry, CCPP suites, or
7779
# generator scripts have been modified, and
7880
# to extract required source code paths:
79-
config.generate_cam_src(gen_indent)
81+
scheme_names = config.generate_cam_src(gen_indent)
8082

8183
dycore = config.get_value('dyn')
82-
phys_suites = config.get_value('physics_suites')
8384
reg_dir = config.get_value('reg_dir')
8485
init_dir = config.get_value('init_dir')
8586
phys_dirs_str = config.get_value('phys_dirs')
@@ -172,17 +173,23 @@ def _build_cam():
172173
if len(optional_mpas_features) > 0:
173174
cmd += " OPTIONAL_MPAS_FEATURES=\"" + " ".join(optional_mpas_features) + "\""
174175

175-
# If the physics suite is MUSICA, build the MUSICA library and get configuration
176-
if phys_suites == "musica":
177-
_build_musica_library(case)
176+
# If MUSICA-CCPP scheme is used in a suite, download
177+
# the MUSICA configuration and build the MUSICA library
178+
if MUSICA_CCPP_SCHEME_NAME in scheme_names:
178179
_download_musica_configuration(caseroot)
180+
musica_install_path = _build_musica(caseroot)
181+
182+
cam_linked_libs = case.get_value("CAM_LINKED_LIBS")
183+
musica_libs = "-lmusica-fortran -lmusica -lyaml-cpp"
184+
if not musica_libs in cam_linked_libs:
185+
_set_musica_lib_path(musica_install_path, caseroot)
179186

180187
cmd += ' USER_INCLDIR="'\
181-
f'-I{os.path.join(bldroot, "musica", "include", "micm")} '\
182-
f'-I{os.path.join(bldroot, "musica", "include", "musica")} '\
183-
f'-I{os.path.join(bldroot, "musica", "include", "musica", "micm")} '\
184-
f'-I{os.path.join(bldroot, "musica", "include", "musica", "tuvx")} '\
185-
f'-I{os.path.join(bldroot, "musica", "include", "musica", "fortran")} '\
188+
f'-I{os.path.join(musica_install_path, "include", "micm")} '\
189+
f'-I{os.path.join(musica_install_path, "include", "musica")} '\
190+
f'-I{os.path.join(musica_install_path, "include", "musica", "micm")} '\
191+
f'-I{os.path.join(musica_install_path, "include", "musica", "tuvx")} '\
192+
f'-I{os.path.join(musica_install_path, "include", "musica", "fortran")} '\
186193
'"'
187194

188195
retcode, out, err = run_cmd(cmd)
@@ -242,74 +249,68 @@ def _copy2_as_needed(src: str, dst: str) -> None:
242249
shutil.copy2(src, dst)
243250

244251
###############################################################################
245-
def _build_musica_library(case: Case) -> None:
252+
def _build_musica(clone_dest: str) -> str:
246253
###############################################################################
247254
"""
248255
Builds and installs the MUSICA library.
249256
250257
Args:
251-
case (Case)
258+
clone_dest: destination where the repository will be cloned
252259
253260
Raises:
254261
Exception: If configuring the CMake MUSICA project fails or
255262
the MUSICA library build fails, an exception is raised.
256263
264+
Returns:
265+
musica_install_path: path to the MUSICA installation directory
257266
"""
258-
install_dir_name = "install"
259-
build_type = "Release"
260-
caseroot = os.path.normpath(case.get_value("CASEROOT"))
261-
_clone_and_checkout(MUSICA_REPO_URL, MUSICA_TAG, caseroot)
267+
_clone_and_checkout(MUSICA_REPO_URL, MUSICA_TAG, clone_dest)
262268

263-
bld_path = os.path.join(caseroot, "musica", "build")
269+
bld_path = os.path.join(clone_dest, "musica", "build")
264270
if os.path.exists(bld_path):
265271
shutil.rmtree(bld_path)
266272
os.makedirs(bld_path)
267273

268-
# To install the target, the working directory must be the build directory.
274+
# To install the target, the working directory must be the build (CMake binary) directory.
269275
current_dir = os.getcwd()
270276
os.chdir(bld_path)
271277

278+
install_dir = "install"
272279
command = [
273280
"cmake",
274-
f"-DCMAKE_INSTALL_PREFIX={install_dir_name}",
275-
f"-DCMAKE_BUILD_TYPE={build_type}",
276-
"-DMUSICA_ENABLE_TESTS=OFF",
277-
"-DMUSICA_BUILD_FORTRAN_INTERFACE=ON",
281+
f"-D CMAKE_INSTALL_PREFIX={install_dir}",
282+
"-D CMAKE_BUILD_TYPE=Release",
283+
"-D MUSICA_ENABLE_TESTS=OFF",
284+
"-D MUSICA_BUILD_FORTRAN_INTERFACE=ON",
278285
".."
279286
]
280-
result = subprocess.run(command, stdout=subprocess.PIPE, stderr=subprocess.PIPE, text=True, check=False)
281-
if result.returncode != 0:
282-
raise Exception(f"Unable to configure the MUSICA CMake project. Error: {result.stderr}")
287+
try:
288+
subprocess.run(command, stdout=subprocess.PIPE, stderr=subprocess.PIPE,
289+
text=True, check=False)
290+
except subprocess.CalledProcessError as e:
291+
raise subprocess.CalledProcessError(e.returncode, e.cmd, "The subprocess \
292+
for cmake to configure the MUSICA CMake project failed.") from e
293+
except FileNotFoundError as e:
294+
raise FileNotFoundError("The 'cmake' command was not found.") from e
295+
except OSError as e:
296+
raise OSError("An error occurred while executing the 'cmake' command.") from e
283297

284298
command = ["cmake", "--build", ".", "--target", "install"]
285-
result = subprocess.run(command, stdout=subprocess.PIPE, stderr=subprocess.PIPE, text=True, check=False)
286-
if result.returncode != 0:
287-
raise Exception(f"Unable to build the MUSICA library. Error: {result.stderr}")
288-
289-
# Create symlinks pointing to the MUSICA libraries in the ATM build directory
290-
atm_bld_root = os.path.join(case.get_value("EXEROOT"), "atm", "obj")
291-
musica_lib_root = os.path.join(atm_bld_root, "musica", "lib")
292-
os.makedirs(musica_lib_root, exist_ok=True)
293-
294-
installed_lib_path = os.path.join(bld_path, install_dir_name, "lib64")
295-
root, _, files = next(os.walk(installed_lib_path, topdown=True))
296-
for file in files:
297-
symlink_force(os.path.join(root, file), os.path.join(musica_lib_root, file))
298-
299-
# Create symlinks pointing to the MUSICA include files in the ATM build directory
300-
musica_include_root = os.path.join(atm_bld_root, "musica", "include")
301-
os.makedirs(musica_include_root, exist_ok=True)
302-
303-
installed_include_path = os.path.join(bld_path, install_dir_name, "include")
304-
for root, _, files in os.walk(installed_include_path):
305-
rel_path = os.path.relpath(root, installed_include_path)
306-
dest_dir = os.path.join(musica_include_root, rel_path)
307-
308-
os.makedirs(dest_dir, exist_ok=True)
309-
for file in files:
310-
symlink_force(os.path.join(root, file), os.path.join(dest_dir, file))
299+
try:
300+
subprocess.run(command, stdout=subprocess.PIPE, stderr=subprocess.PIPE,
301+
text=True, check=False)
302+
except subprocess.CalledProcessError as e:
303+
raise subprocess.CalledProcessError(e.returncode, e.cmd, "The subprocess \
304+
for cmake to build the MUSICA library failed.") from e
305+
except FileNotFoundError as e:
306+
raise FileNotFoundError("The 'cmake' command was not found.") from e
307+
except OSError as e:
308+
raise OSError("An error occurred while executing the 'cmake' command.") from e
311309

312310
os.chdir(current_dir)
311+
musica_install_path = os.path.join(bld_path, install_dir)
312+
313+
return musica_install_path
313314

314315
###############################################################################
315316
def _download_musica_configuration(download_dest: str) -> None:
@@ -319,7 +320,7 @@ def _download_musica_configuration(download_dest: str) -> None:
319320
directory to match the name in the MUSICA-CCPP configuration.
320321
321322
Args:
322-
download_dest: destination where configuration will be downloaded.
323+
download_dest: destination where configuration will be downloaded
323324
324325
Raises:
325326
Exception: If the directory to be renamed is not found or
@@ -334,14 +335,14 @@ def _download_musica_configuration(download_dest: str) -> None:
334335
renamed_dir = os.path.join(download_dest, "cam-sima-chemistry-data", musica_config_dir_name)
335336
try:
336337
os.rename(original_dir, renamed_dir)
337-
except FileNotFoundError:
338-
raise FileNotFoundError(f"The directory '{original_dir}' was not found.")
339-
except FileExistsError:
340-
raise FileExistsError(f"The destination directory '{renamed_dir}' already exists.")
341-
except PermissionError:
342-
raise PermissionError(f"Permission denied to rename '{original_dir}'.")
338+
except FileNotFoundError as e:
339+
raise FileNotFoundError(f"The directory '{original_dir}' was not found.") from e
340+
except FileExistsError as e:
341+
raise FileExistsError(f"The destination directory '{renamed_dir}' already exists.") from e
342+
except PermissionError as e:
343+
raise PermissionError(f"Permission denied to rename '{original_dir}'.") from e
343344
except OSError as e:
344-
raise OSError(f"An error occurred while renaming: {e}")
345+
raise OSError("An error occurred while renaming.") from e
345346

346347
musica_config_path = os.path.join(download_dest, musica_config_dir_name)
347348
if os.path.exists(musica_config_path):
@@ -350,15 +351,58 @@ def _download_musica_configuration(download_dest: str) -> None:
350351
shutil.move(renamed_dir, download_dest)
351352

352353
###############################################################################
353-
def _clone_and_checkout(repo_url: str, tag_name: str, clone_dest: str):
354+
def _set_musica_lib_path(musica_install_path: str, caseroot: str) -> None:
355+
###############################################################################
356+
"""
357+
Sets the MUSICA libraries path to CAM_LINKED_LIBS, allowing the libraries
358+
to be linked during the CESM build process.
359+
360+
Args:
361+
musica_install_path: path to the MUSICA installation directory
362+
caseroot: CASEROOT where the xmlchange command is located
363+
364+
Raises:
365+
Exception: If the subprocess for the xmlchange command fails,
366+
an exception is raised with the error message.
367+
"""
368+
369+
current_dir = os.getcwd()
370+
os.chdir(caseroot)
371+
372+
unlock_file("env_build.xml", caseroot)
373+
374+
command = [
375+
"./xmlchange",
376+
"--append",
377+
# The libraries must be on the same line because CIME flags an
378+
# error for multi-character arguments preceded by a single dash
379+
f"CAM_LINKED_LIBS=-L{os.path.join(musica_install_path, 'lib64')} -lmusica-fortran -lmusica -lyaml-cpp"
380+
]
381+
try:
382+
subprocess.run(command, stdout=subprocess.PIPE, stderr=subprocess.PIPE,
383+
text=True, check=False)
384+
except subprocess.CalledProcessError as e:
385+
raise subprocess.CalledProcessError(e.returncode, e.cmd, "The subprocess \
386+
for xmlchange to set the MUSICA library path failed.") from e
387+
except FileNotFoundError as e:
388+
raise FileNotFoundError("The 'xmlchange' command was not found.") from e
389+
except OSError as e:
390+
raise OSError("An error occurred while executing the 'xmlchange' command.") from e
391+
392+
os.chdir(current_dir)
393+
394+
lock_file("env_build.xml", caseroot)
395+
396+
###############################################################################
397+
def _clone_and_checkout(repo_url: str, tag_name: str, clone_dest: str) -> None:
354398
###############################################################################
355399
"""
356400
Clones a Git repository from the URL and checks out a specific branch.
357401
358402
Args:
359-
repo_url: The URL of the Git repository to clone
360-
tag_name: The tag name to check out
361-
clone_dest: destination where the repository will be cloned.
403+
repo_url: URL of the Git repository to clone
404+
tag_name: tag name to check out
405+
clone_dest: destination where the repository will be cloned
362406
363407
Raises:
364408
Exception: If the `git clone` or `git checkout` commands fail,
@@ -370,17 +414,27 @@ def _clone_and_checkout(repo_url: str, tag_name: str, clone_dest: str):
370414
if os.path.exists(repo_path):
371415
shutil.rmtree(repo_path)
372416

373-
result = subprocess.run(["git", "clone", repo_url, repo_path],
374-
stderr=subprocess.PIPE, text=True, check=False)
375-
if result.returncode != 0:
376-
raise Exception(f"Unable to clone the repository: {repo_url}. \
377-
Error: {result.stderr}")
378-
379-
result = subprocess.run(["git", "-C", repo_path, "checkout", tag_name],
380-
stderr=subprocess.PIPE, text=True, check=False)
381-
if result.returncode != 0:
382-
raise Exception(f"Unable to checkout the branch: {tag_name}. \
383-
Error: {result.stderr}")
417+
try:
418+
subprocess.run(["git", "clone", repo_url, repo_path],
419+
stdout=subprocess.PIPE, stderr=subprocess.PIPE, text=True, check=False)
420+
except subprocess.CalledProcessError as e:
421+
raise subprocess.CalledProcessError(e.returncode, e.cmd, f"The subprocess \
422+
for git to clone the repository {repo_url} failed.") from e
423+
except FileNotFoundError as e:
424+
raise FileNotFoundError("The 'git' command was not found.") from e
425+
except OSError as e:
426+
raise OSError("An error occurred while executing the 'git' command.") from e
427+
428+
try:
429+
subprocess.run(["git", "-C", repo_path, "checkout", tag_name],
430+
stdout=subprocess.PIPE, stderr=subprocess.PIPE, text=True, check=False)
431+
except subprocess.CalledProcessError as e:
432+
raise subprocess.CalledProcessError(e.returncode, e.cmd, f"The subprocess \
433+
for git to checkout the branch {tag_name} failed.") from e
434+
except FileNotFoundError as e:
435+
raise FileNotFoundError("The 'git' command was not found.") from e
436+
except OSError as e:
437+
raise OSError("An error occurred while executing the 'git' command.") from e
384438

385439
###############################################################################
386440

cime_config/buildnml

+1-1
Original file line numberDiff line numberDiff line change
@@ -166,7 +166,7 @@ def buildnml(case, caseroot, compname):
166166
gen_indent = 3
167167

168168
#Generate model code and meta-data:
169-
config.generate_cam_src(gen_indent)
169+
_ = config.generate_cam_src(gen_indent)
170170

171171
#----------------------------------------------------------------
172172
# Create namelist attribute dictionary (to set namelist defaults):

cime_config/cam_autogen.py

+4-1
Original file line numberDiff line numberDiff line change
@@ -452,6 +452,7 @@ def generate_physics_suites(build_cache, preproc_defs, host_name,
452452
# Find the SDFs specified for this model build
453453
sdfs = []
454454
scheme_files = []
455+
scheme_names = set()
455456
xml_files = {} # key is scheme, value is xml file path
456457
for sdf in phys_suites_str.split(';'):
457458
sdf_path = _find_file(f"suite_{sdf}.xml", suite_search)
@@ -467,6 +468,8 @@ def generate_physics_suites(build_cache, preproc_defs, host_name,
467468
# Given an SDF, find all the schemes it calls
468469
_, suite = read_xml_file(sdf_path)
469470
sdf_schemes = _find_schemes_in_sdf(suite)
471+
#Add schemes to set of all scheme names:
472+
scheme_names.update(sdf_schemes)
470473
# For each scheme, find its metadata file
471474
for scheme in sdf_schemes:
472475
if scheme in all_scheme_files:
@@ -655,7 +658,7 @@ def generate_physics_suites(build_cache, preproc_defs, host_name,
655658
# End if
656659

657660
return [physics_blddir, genccpp_dir], do_gen_ccpp, cap_output_file, \
658-
xml_files.values(), capgen_db
661+
xml_files.values(), capgen_db, scheme_names
659662

660663
###############################################################################
661664
def generate_init_routines(build_cache, bldroot, force_ccpp, force_init,

cime_config/cam_config.py

+4-1
Original file line numberDiff line numberDiff line change
@@ -868,7 +868,7 @@ def generate_cam_src(self, gen_fort_indent):
868868
self.__atm_root, self.__bldroot,
869869
reg_dir, reg_files, source_mods_dir,
870870
force_ccpp)
871-
phys_dirs, force_init, _, nml_fils, capgen_db = retvals
871+
phys_dirs, force_init, _, nml_fils, capgen_db, scheme_names = retvals
872872

873873
# Add namelist definition files to dictionary:
874874
for nml_fil in nml_fils:
@@ -899,6 +899,9 @@ def generate_cam_src(self, gen_fort_indent):
899899
#--------------------------------------------------------------
900900
build_cache.write()
901901

902+
#Return the set of all scheme names present in the SDFs:
903+
return scheme_names
904+
902905
#++++++++++++++++++++++++
903906

904907
def ccpp_phys_set(self, cam_nml_attr_dict, phys_nl_pg_dict):

docker/Dockerfile.musica

+3-4
Original file line numberDiff line numberDiff line change
@@ -75,19 +75,18 @@ ENV ESMFMKFILE=/usr/local/lib/esmf.mk
7575
RUN /home/cam_sima_user/CAM-SIMA/cime/scripts/create_newcase --case $CASE_NAME \
7676
--compset FPHYStest --res ne5_ne5_mg37 --run-unsupported
7777

78-
WORKDIR $CASE_NAME
78+
WORKDIR $CASE_NAME
7979

8080
RUN ./case.setup
8181

8282
# Set the GLC timestep to match the atmosphere timestep
8383
RUN ./xmlchange GLC_NCP=48
8484
RUN ./xmlchange ATM_NCPL=48
85+
RUN ./xmlchange CAM_CONFIG_OPTS="--dyn none --physics-suites musica"
8586
RUN ./xmlchange STOP_OPTION=nsteps
8687
RUN ./xmlchange STOP_N=5
87-
RUN ./xmlchange CAM_CONFIG_OPTS="--dyn none --physics-suites musica"
88-
RUN ./xmlchange CAM_LINKED_LIBS="-L/home/cam_sima_user/scratch/test-case/bld/atm/obj/musica/lib -lmusica-fortran -lmusica -lyaml-cpp"
8988

9089
# Avoid writing restart files
9190
RUN ./xmlchange REST_N=100
9291

93-
RUN ./case.build
92+
RUN ./case.build

0 commit comments

Comments
 (0)