# Copyright 2013-2024 Lawrence Livermore National Security, LLC and other # Spack Project Developers. See the top-level COPYRIGHT file for details. # # SPDX-License-Identifier: (Apache-2.0 OR MIT) import glob import os from spack.package import * class Camx(MakefilePackage): """Comprehensive Air Quality Model with Extensions.""" homepage = "https://www.camx.com" # Upstream obfuscates their download URL to get you to fill out their # registration form and accept their license agreement. version( "6.50", url="http://www.camx.com/getmedia/caaf7983-616b-4207-bd10-c2b404bda78d/CAMx_v6-50-src-180430.tgz", sha256="4a53f78e0722d85a9c7d8ed6732aff55163a4ce06f69b6bbc9e00a3bf36a756c", ) resource( when="@6.50", name="user_manual", url="http://www.camx.com/files/camxusersguide_v6-50.pdf", sha256="b02d9826d59f22f9daa5955bb7b9fd3e0ca86eb73017c5845896d40391c64588", expand=False, placement="doc", ) resource( when="@6.50", name="input_data", url="http://www.camx.com/getmedia/77ad8028-9388-4f5d-bcab-a418e15dde68/v6-50-specific-inputs-180430.tgz", sha256="89b58283e37b8e2bd550a8ec62208f241be72c78dc26da9c42ad63c34f54ebfb", placement="data", ) variant("mpi", default=True, description="Enable MPI") variant( "threads", default="pthread", description="Multithreading support", values=("pthread", "openmp"), multi=False, ) depends_on("mpi", when="+mpi") parallel = False def patch(self): # Relax values in parameter file to fix fun errors like this: # # ERROR in STARTUP: # A parameter in the camx.prm is not sufficiently large. # Please change the value for parameter: MXCELLS # It should be set to a value of at least: 396 with working_dir("Includes"): duplicate = "camx.prm" os.remove(duplicate) orig = "camx.prm.v{0}".format(self.spec.version) prm = FileFilter(orig) prm.filter(r"MXCELLS = [^)]+", "MXCELLS = 400 ") prm.filter(r"MXPTSRC = [^)]+", "MXPTSRC = 1355961 ") prm.filter(r"MXLAYER = [^)]+", "MXLAYER = 40 ") prm.filter(r"MXPIG = [^)]+", "MXPIG = 100000 ") def edit(self, spec, prefix): makefile = FileFilter("Makefile") if spec.target.family == "aarch64": makefile.filter("-mcmodel=medium", "-mcmodel=large") makefile = FileFilter("./MPI/util/Makefile") makefile.filter("-mcmodel=medium", "-mcmodel=large") # Support Intel MPI. if spec["mpi"].name == "intel-mpi": makefile.filter( "else ifneq (, $(findstring $(MPI),openmpi openMPI OPENMPI))", """else ifneq (, $(findstring $(MPI),intel-mpi intel impi)) MPI_ECHO = "* MPI will be built in using Intel MPI" MPI_INC = $(MPI_INST)/include MPI_LIBS = -L$(CAMX_MPI)/util -lutil -lparlib """ + spec["mpi"].libs.ld_flags + """ MPI_STRING = IntelMPI FC = $(MPI_INST)/bin64/mpifort CC = $(MPI_INST)/bin64/mpicc else ifneq (, $(findstring $(MPI),openmpi openMPI OPENMPI))""", string=True, ) makefile.filter("OPENMPI MVAPICH", "OPENMPI MVAPICH IntelMPI", string=True) if "+mpi" in spec: # Substitute CC, FC. makefile.filter("CC = .*", "CC = " + spec["mpi"].mpicc) makefile.filter("FC = .*", "FC = " + spec["mpi"].mpifc) # Fix parlib not being compiled. makefile.filter( "all: comp_$(COMPILER)", # Yes, flake8, Makefile rules needs tabs! """all: parlib comp_$(COMPILER) parlib : $(MAKE) -C $(CAMX_MPI)/util # noqa: E101,W191 """, string=True, ) # noqa: E101 makefile_parlib = FileFilter("MPI/util/Makefile") makefile_parlib.filter("CC = .*", "CC = " + spec["mpi"].mpicc) makefile_parlib.filter("LIBS = .*", "LIBS = " + spec["mpi"].libs.ld_flags) makefile_parlib.filter("MPI_INST = .*", "MPI_INST = " + spec["mpi"].prefix) else: # Substitute CC, FC. makefile.filter("CC = .*", "CC = " + env["CC"]) makefile.filter("FC = .*", "FC = " + env["FC"]) @property def build_targets(self): # Set compiler. omp = ["", "omp"][self.spec.satisfies("threads=openmp")] compiler = os.path.basename(env["FC"]) + omp args = ["COMPILER={0}".format(compiler)] # Set MPI. if "+mpi" in self.spec: mpi = self.spec["mpi"] args += ["MPI={0}".format(mpi.name), "MPI_INST={0}".format(mpi.prefix)] return args def install(self, spec, prefix): exe = glob.glob("CAMx.*")[0] mkdir(prefix.bin) install(exe, prefix.bin.camx) mkdirp(prefix.share.doc) install_tree("doc", prefix.share.doc, symlinks=False) mkdir(prefix.share.data) install_tree("data", prefix.share.data)