Skip to content

Commit a18b712

Browse files
committed
Add chemistry support to MFC with Spack compatibility
This commit enables thermochemistry in MFC via a new MFC_CHEMISTRY CMake option and adds full Spack package support for the chemistry variant. Changes: CMake (CMakeLists.txt): - Add MFC_CHEMISTRY option (default OFF) to enable chemistry - Add MFC_MECH_FILE cache variable for specifying Cantera mechanism files - Pass chemistry flag to Fypp preprocessor as -D chemistry=True/False - Add Python3 requirement when MFC_CHEMISTRY=ON - Generate m_thermochem.f90 via gen_thermochem.py during build Python (toolchain/scripts/gen_thermochem.py): - New script to generate Fortran chemistry module using Pyrometheus and Cantera - Called by CMake when MFC_CHEMISTRY=ON - Supports custom mechanism files via --mech argument Fortran (src/common/m_derived_types.fpp): - Guard m_thermochem import with #:if chemistry - Move fallback num_species declaration after implicit none - Fixes "IMPLICIT NONE after data declaration" error Build system (toolchain/mfc/build.py): - Auto-enable MFC_CHEMISTRY=ON when case requests chemistry - Pass MFC_MECH_FILE when cantera_file is specified in case Spack (packaging/spack/package.py): - Add chemistry variant (default False) - Add cantera+python dependency when +chemistry - Vendor Pyrometheus 1.0.5 as a resource - Pass MFC_CHEMISTRY to CMake via cmake_args - Add Pyrometheus to PYTHONPATH in setup_build_environment Documentation (packaging/spack/SPACK.md): - Document chemistry variant and usage - List chemistry dependencies This preserves backward compatibility: existing builds work unchanged (chemistry OFF by default), while enabling reproducible chemistry builds via Spack with "spack install mfc+chemistry".
1 parent c341252 commit a18b712

File tree

6 files changed

+143
-3
lines changed

6 files changed

+143
-3
lines changed

CMakeLists.txt

Lines changed: 37 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,7 @@ option(MFC_SYSCHECK "Build syscheck" OFF
2929
option(MFC_DOCUMENTATION "Build documentation" OFF)
3030
option(MFC_ALL "Build everything" OFF)
3131
option(MFC_SINGLE_PRECISION "Build single precision" OFF)
32+
option(MFC_CHEMISTRY "Enable thermochemistry" OFF)
3233

3334
if (MFC_ALL)
3435
set(MFC_PRE_PROCESS ON FORCE)
@@ -43,6 +44,15 @@ else()
4344
add_compile_definitions(MFC_DOUBLE_PRECISION)
4445
endif()
4546

47+
# Chemistry configuration
48+
set(_chem_str "False")
49+
if (MFC_CHEMISTRY)
50+
set(_chem_str "True")
51+
endif()
52+
53+
# Optional: mechanism file name/path for Cantera
54+
set(MFC_MECH_FILE "" CACHE STRING "Cantera mechanism (YAML) file name or path")
55+
4656

4757
# CMake Library Imports
4858

@@ -90,6 +100,11 @@ endif()
90100

91101
find_program(FYPP_EXE fypp REQUIRED)
92102

103+
# Python3 is required for chemistry code generation when MFC_CHEMISTRY is ON
104+
if (MFC_CHEMISTRY)
105+
find_package(Python3 REQUIRED COMPONENTS Interpreter)
106+
endif()
107+
93108

94109
# Miscellaneous Configuration:
95110
# * Explicitly link to -ldl (or system equivalent)
@@ -367,7 +382,7 @@ macro(HANDLE_SOURCES target useCommon)
367382
-D MFC_${${target}_UPPER}
368383
-D MFC_COMPILER="${CMAKE_Fortran_COMPILER_ID}"
369384
-D MFC_CASE_OPTIMIZATION=False
370-
-D chemistry=False
385+
-D chemistry=${_chem_str}
371386
--line-numbering
372387
--no-folding
373388
--line-length=999
@@ -380,6 +395,27 @@ macro(HANDLE_SOURCES target useCommon)
380395

381396
list(APPEND ${target}_SRCs ${f90})
382397
endforeach()
398+
399+
# Generate m_thermochem.f90 when chemistry is enabled
400+
if (MFC_CHEMISTRY)
401+
file(MAKE_DIRECTORY "${CMAKE_BINARY_DIR}/modules/${target}")
402+
403+
add_custom_command(
404+
OUTPUT "${CMAKE_BINARY_DIR}/modules/${target}/m_thermochem.f90"
405+
COMMAND ${CMAKE_COMMAND} -E make_directory "${CMAKE_BINARY_DIR}/modules/${target}"
406+
COMMAND ${Python3_EXECUTABLE}
407+
"${CMAKE_SOURCE_DIR}/toolchain/scripts/gen_thermochem.py"
408+
--module m_thermochem
409+
--out "${CMAKE_BINARY_DIR}/modules/${target}/m_thermochem.f90"
410+
--mech "${MFC_MECH_FILE}"
411+
DEPENDS "${CMAKE_SOURCE_DIR}/toolchain/scripts/gen_thermochem.py"
412+
COMMENT "Generating m_thermochem.f90 via Pyrometheus/Cantera for ${target}"
413+
VERBATIM
414+
)
415+
416+
list(APPEND ${target}_SRCs
417+
"${CMAKE_BINARY_DIR}/modules/${target}/m_thermochem.f90")
418+
endif()
383419
endmacro()
384420

385421

packaging/spack/SPACK.md

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -88,6 +88,12 @@ spack install mfc~post_process # Skip post_process binary
8888
```
8989
Controls whether the post-processing tool is built.
9090

91+
#### Chemistry (default: disabled)
92+
```
93+
spack install mfc+chemistry
94+
```
95+
Enables thermochemistry support by generating the `m_thermochem.f90` module using Pyrometheus and Cantera during the build. When enabled, MFC can perform reactive flow simulations with detailed chemical kinetics. Requires `cantera+python` and vendors Pyrometheus automatically.
96+
9197
### Dependencies
9298

9399
Build-time dependencies (required during compilation):
@@ -103,6 +109,8 @@ Optional dependencies (variant-controlled):
103109
- mpi - Message Passing Interface (when +mpi)
104110
- silo - Silo data format with HDF5 support (when +post_process)
105111
- hdf5 - HDF5 data format (transitive dependency via Silo when +post_process)
112+
- cantera+python - Thermochemical kinetics library (when +chemistry)
113+
- pyrometheus - Fortran code generator for chemistry (vendored automatically when +chemistry)
106114
- cuda - NVIDIA CUDA toolkit (when +openacc or +openmp with NVHPC)
107115
- hip - AMD ROCm HIP (when +openacc or +openmp with Cray)
108116

packaging/spack/package.py

Lines changed: 18 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -36,13 +36,24 @@ class Mfc(CMakePackage):
3636
description="Floating point precision",
3737
)
3838
variant("post_process", default=True, description="Build post-processing tool")
39+
variant("chemistry", default=False, description="Enable thermochemistry via Pyrometheus/Cantera")
3940

4041
# Required dependencies
4142
depends_on("[email protected]:", type="build")
4243
depends_on("py-fypp", type="build")
4344
depends_on("python@3:", type="build")
44-
# Note: py-cantera is not yet available in Spack builtin repo
45-
# Will add when chemistry variant is implemented
45+
46+
# Chemistry dependencies
47+
depends_on("cantera+python", type="build", when="+chemistry")
48+
# Note: py-pyrometheus may not be in Spack yet; will be added to PYTHONPATH via resource
49+
resource(
50+
name="pyrometheus",
51+
url="https://files.pythonhosted.org/packages/21/77/1e48bef25dfef5d9e35c1ab3a3a2ea1c82adb59aceb82b18d13b3d6c8a2b/pyrometheus-1.0.5.tar.gz",
52+
sha256="a572ab6db954f4a850d1292bb1ef6d6055916784a894d149d657996fa98d0367",
53+
when="+chemistry",
54+
placement="pydeps/pyrometheus",
55+
expand=True
56+
)
4657

4758
# Runtime dependencies
4859
depends_on("fftw@3:", when="~mpi")
@@ -80,6 +91,7 @@ def cmake_args(self):
8091
self.define("MFC_PRE_PROCESS", True),
8192
self.define("MFC_SIMULATION", True),
8293
self.define_from_variant("MFC_POST_PROCESS", "post_process"),
94+
self.define_from_variant("MFC_CHEMISTRY", "chemistry"),
8395
]
8496

8597
if self.spec.variants["precision"].value == "single":
@@ -90,3 +102,7 @@ def cmake_args(self):
90102
def setup_build_environment(self, env):
91103
# Fypp is required for preprocessing
92104
env.prepend_path("PATH", self.spec["py-fypp"].prefix.bin)
105+
106+
# Make vendored Pyrometheus importable when chemistry is enabled
107+
if "+chemistry" in self.spec:
108+
env.prepend_path("PYTHONPATH", join_path(self.stage.source_path, "pydeps", "pyrometheus"))

src/common/m_derived_types.fpp

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,10 +11,16 @@ module m_derived_types
1111
use m_constants !< Constants
1212

1313
use m_precision_select
14+
#:if chemistry
1415
use m_thermochem, only: num_species
16+
#:endif
1517

1618
implicit none
1719

20+
#:if not chemistry
21+
integer, parameter :: num_species = 1
22+
#:endif
23+
1824
!> Derived type adding the field position (fp) as an attribute
1925
type field_position
2026
real(wp), allocatable, dimension(:, :, :) :: fp !< Field position

toolchain/mfc/build.py

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -153,6 +153,14 @@ def configure(self, case: Case):
153153
flags.append(f"-DMFC_Unified={'ON' if ARG('unified') else 'OFF'}")
154154
flags.append(f"-DMFC_Fastmath={'ON' if ARG('fastmath') else 'OFF'}")
155155

156+
# Enable chemistry flags when requested by the case
157+
if case.params.get('chemistry', 'F') == 'T':
158+
flags.append("-DMFC_CHEMISTRY=ON")
159+
# Pass mechanism if provided; otherwise Cantera defaults (e.g., h2o2.yaml)
160+
mech = case.params.get("cantera_file", "")
161+
if mech:
162+
flags.append(f"-DMFC_MECH_FILE={mech}")
163+
156164
command = ["cmake"] + flags + ["-S", cmake_dirpath, "-B", build_dirpath]
157165

158166
delete_directory(build_dirpath)
Lines changed: 66 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,66 @@
1+
#!/usr/bin/env python3
2+
"""
3+
Generate m_thermochem.f90 module using Pyrometheus and Cantera.
4+
5+
This script is called by CMake when MFC_CHEMISTRY=ON to generate
6+
the Fortran thermochemistry module from a Cantera mechanism file.
7+
"""
8+
import argparse
9+
import sys
10+
11+
def main():
12+
parser = argparse.ArgumentParser(
13+
description="Generate Fortran thermochemistry module via Pyrometheus"
14+
)
15+
parser.add_argument("--module", required=True, help="Module name (e.g., m_thermochem)")
16+
parser.add_argument("--out", required=True, help="Output Fortran file path")
17+
parser.add_argument(
18+
"--mech",
19+
default="h2o2.yaml",
20+
help="Cantera mechanism file (YAML). Defaults to h2o2.yaml"
21+
)
22+
args = parser.parse_args()
23+
24+
try:
25+
import cantera as ct
26+
except ImportError:
27+
print("ERROR: cantera Python package is required for chemistry.", file=sys.stderr)
28+
print("Install it with: pip install cantera", file=sys.stderr)
29+
sys.exit(1)
30+
31+
try:
32+
import pyrometheus as pyro
33+
except ImportError:
34+
print("ERROR: pyrometheus Python package is required for chemistry.", file=sys.stderr)
35+
print("Install it with: pip install pyrometheus", file=sys.stderr)
36+
sys.exit(1)
37+
38+
# Load the Cantera solution
39+
# If no mechanism file is specified or it's empty, use Cantera's default h2o2.yaml
40+
mech_file = args.mech if args.mech else "h2o2.yaml"
41+
42+
try:
43+
sol = ct.Solution(mech_file)
44+
except Exception as e:
45+
print(f"ERROR: Failed to load Cantera mechanism '{mech_file}': {e}", file=sys.stderr)
46+
sys.exit(1)
47+
48+
# Generate Fortran code using Pyrometheus
49+
try:
50+
code = pyro.FortranCodeGenerator().generate(args.module, sol)
51+
except Exception as e:
52+
print(f"ERROR: Failed to generate Fortran code: {e}", file=sys.stderr)
53+
sys.exit(1)
54+
55+
# Write to output file
56+
try:
57+
with open(args.out, 'w') as f:
58+
f.write(code)
59+
print(f"Successfully generated {args.out} from {mech_file}")
60+
except Exception as e:
61+
print(f"ERROR: Failed to write output file '{args.out}': {e}", file=sys.stderr)
62+
sys.exit(1)
63+
64+
if __name__ == "__main__":
65+
main()
66+

0 commit comments

Comments
 (0)