Skip to content

Commit 2cc15b4

Browse files
committed
Added tests for Python extension and updated build system for Python extension
Add methods for reading files into Variables that can be used by the python extension Add debug messages for duplicate input parameters and for absent EnzymeAct when useC3 is true
1 parent 657ec3d commit 2cc15b4

File tree

15 files changed

+592
-231
lines changed

15 files changed

+592
-231
lines changed

.github/workflows/test-suite.yml

Lines changed: 39 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@ jobs:
2222
with-elf: [false]
2323
with-valgrind: [false, true]
2424
with-mpi: [false]
25+
with-python: [false]
2526
include:
2627
- sundials-version: "5.7.0"
2728
os: ubuntu-latest
@@ -30,41 +31,55 @@ jobs:
3031
with-elf: false
3132
with-valgrind: false
3233
with-mpi: false
34+
with-python: false
3335
- sundials-version: "6.7.0"
3436
os: ubuntu-latest
3537
with-coverage: false
3638
with-asan: false
3739
with-elf: false
3840
with-valgrind: false
3941
with-mpi: false
42+
with-python: false
4043
- sundials-version: "7.1.1"
4144
os: ubuntu-latest
4245
with-coverage: false
4346
with-asan: false
4447
with-elf: false
4548
with-valgrind: false
4649
with-mpi: false
50+
with-python: false
4751
- sundials-version: latest
4852
os: ubuntu-latest
4953
with-coverage: false
5054
with-asan: false
5155
with-elf: false
5256
with-valgrind: false
5357
with-mpi: true
58+
with-python: false
5459
- sundials-version: "6.7.0"
5560
os: ubuntu-latest
5661
with-coverage: false
5762
with-asan: false
5863
with-elf: false
5964
with-valgrind: false
6065
with-mpi: true
66+
with-python: false
6167
- sundials-version: "7.1.1"
6268
os: ubuntu-latest
6369
with-coverage: false
6470
with-asan: false
6571
with-elf: false
6672
with-valgrind: false
6773
with-mpi: true
74+
with-python: false
75+
- sundials-version: latest
76+
os: ubuntu-latest
77+
with-coverage: false
78+
with-asan: false
79+
with-elf: false
80+
with-valgrind: false
81+
with-mpi: false
82+
with-python: true
6883
exclude:
6984
- os: windows-latest
7085
with-asan: true
@@ -212,6 +227,11 @@ jobs:
212227
echo "CMAKE_ARGS=-DCMAKE_VERBOSE_MAKEFILE:BOOL=ON" >> "$GITHUB_ENV"
213228
echo "CMAKE_ARGS_TEST=-DBUILD_TESTS=ON" >> "$GITHUB_ENV"
214229
echo "TEST_FLAGS=-C ${CMAKE_BUILD_TYPE_TEST} --output-on-failure -VV" >> "$GITHUB_ENV"
230+
echo "BUILD_ARGS=--config ${CMAKE_BUILD_TYPE_TEST}" >> "$GITHUB_ENV"
231+
- name: Set flags to build in parallel (UNIX)
232+
if: matrix.os != 'windows-latest'
233+
run: |
234+
echo "BUILD_ARGS=${{ env.BUILD_ARGS }} -- -j 4" >> "$GITHUB_ENV"
215235
- name: Coverage config flags
216236
if: matrix.with-coverage == true && matrix.os != 'windows-latest'
217237
run: |
@@ -257,21 +277,15 @@ jobs:
257277
mkdir build
258278
cd build
259279
which cmake
260-
cmake .. ${{ env.CMAKE_ARGS }} ${{ env.CMAKE_ARGS_NO_PYTHON }} ${{ env.CMAKE_ARGS_TEST }}
280+
cmake .. ${{ env.CMAKE_ARGS }} ${{ env.CMAKE_ARGS_TEST }}
261281
262282
###################################
263283
# Build CXX
264284
###################################
265-
- name: Build C++ & C in parallel (CONDA, UNIX)
266-
if: matrix.os != 'windows-latest'
285+
- name: Build C++ & C
267286
run: |
268287
cd build
269-
cmake --build . --config ${{ env.CMAKE_BUILD_TYPE_TEST }} -- -j 4
270-
- name: Build C++ & C in serial (CONDA, WINDOWS)
271-
if: matrix.os == 'windows-latest'
272-
run: |
273-
cd build
274-
cmake --build . --config ${{ env.CMAKE_BUILD_TYPE_TEST }}
288+
cmake --build . ${{ env.BUILD_ARGS }}
275289
276290
###################################
277291
# Test CXX
@@ -299,26 +313,25 @@ jobs:
299313
###################################
300314
# Build Python
301315
###################################
302-
# - name: Set CMAKE_ARGS for Python build
303-
# run: |
304-
# echo "OLD_CMAKE_ARGS=${{ env.CMAKE_ARGS }}" >> "$GITHUB_ENV"
305-
# echo "CMAKE_ARGS=${{ env.CMAKE_ARGS }} -DBUILD_PYTHON=ON" >> "$GITHUB_ENV"
306-
# - name: Build Python (CONDA)
307-
# run: |
308-
# cd build
309-
# which cmake
310-
# cmake .. ${{ env.CMAKE_ARGS }} ${{ env.CMAKE_ARGS_TEST }}
311-
# - name: Unset CMAKE_ARGS for Python build
312-
# run: |
313-
# echo "CMAKE_ARGS=${{ env.OLD_CMAKE_ARGS }}" >> "$GITHUB_ENV"
316+
- name: Configure Python
317+
if: matrix.with-python == true
318+
run: |
319+
cd build
320+
cmake .. -DBUILD_PYTHON=ON ${{ env.CMAKE_ARGS }}
321+
- name: Build & install Python
322+
if: matrix.with-python == true
323+
run: |
324+
cd build
325+
cmake --build . --target pyPhotosynthesis ${{ env.BUILD_ARGS }}
326+
cmake --install . --prefix /home/runner/miniconda3/envs/ephoto
314327
315328
###################################
316329
# Test Python
317330
###################################
318-
# - name: Test Python Interfaces (CONDA)
319-
# if: matrix.with-elf == false && matrix.with-valgrind == false
320-
# run: |
321-
# pytest -sv tests/python
331+
- name: Test Python Interfaces
332+
if: matrix.with-python == true
333+
run: |
334+
pytest -sv tests/python
322335
323336
###################################
324337
# Docs tests
@@ -335,7 +348,7 @@ jobs:
335348
if: matrix.with-coverage == true && matrix.os != 'windows-latest'
336349
uses: codecov/codecov-action@v4
337350
with:
338-
name: ${{ matrix.os }}-${{ matrix.with-coverage }}-${{ matrix.with-asan }}-${{ matrix.with-elf }}-${{ matrix.with-valgrind }}
351+
name: ${{ matrix.os }}-${{ matrix.sundials-version }}-${{ matrix.with-coverage }}-${{ matrix.with-asan }}-${{ matrix.with-elf }}-${{ matrix.with-valgrind }}-${{ matrix.with-mpi }}-${{ matrix.with-python }}
339352
token: ${{ secrets.CODECOV_TOKEN }}
340353
file: coverage/coverage.info
341354
functionalities: gcov

.gitignore

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -61,4 +61,6 @@ Doxyfile
6161
doc/html
6262
doc/latex
6363

64-
*~
64+
*~
65+
66+
__pycache__

CMakeLists.txt

Lines changed: 57 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -63,7 +63,7 @@ else()
6363
endif()
6464
# Prohibit in-source build
6565
if("${CMAKE_SOURCE_DIR}" STREQUAL "${CMAKE_BINARY_DIR}")
66-
message(FATAL_ERROR "In-source build prohibited.")
66+
message(FATAL_ERROR "In-source build prohibited.\nCMAKE_SOURCE_DIR = ${CMAKE_SOURCE_DIR}\nCMAKE_BINARY_DIR = ${CMAKE_BINARY_DIR}")
6767
endif("${CMAKE_SOURCE_DIR}" STREQUAL "${CMAKE_BINARY_DIR}")
6868

6969
set(CMAKE_MODULE_PATH "${CMAKE_MODULE_PATH};${PROJECT_SOURCE_DIR}/cmake")
@@ -243,12 +243,14 @@ install(
243243
ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR}
244244
RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR}
245245
PUBLIC_HEADER DESTINATION EPHOTOSYNTHESIS_INSTALL_INCLUDEDIR
246+
COMPONENT CXX
246247
)
247248
install(
248249
EXPORT ePhotosynthesisTargets
249250
FILE ePhotosynthesisTargets.cmake
250251
NAMESPACE ePhotosynthesis::
251252
DESTINATION ${CMAKE_INSTALL_LIBDIR}/cmake/ePhotosynthesis
253+
COMPONENT CXX
252254
)
253255
include(CMakePackageConfigHelpers)
254256
write_basic_package_version_file(
@@ -264,10 +266,12 @@ install(
264266
FILES "${CMAKE_CURRENT_BINARY_DIR}/ePhotosynthesisConfig.cmake"
265267
"${CMAKE_CURRENT_BINARY_DIR}/ePhotosynthesisConfigVersion.cmake"
266268
DESTINATION ${CMAKE_INSTALL_LIBDIR}/cmake/ePhotosynthesis
269+
COMPONENT CXX
267270
)
268271
install(
269272
FILES ${CMAKE_CURRENT_BINARY_DIR}/ePhotosynthesis_export.h
270273
DESTINATION ${EPHOTOSYNTHESIS_INSTALL_INCLUDEDIR}
274+
COMPONENT CXX
271275
)
272276
# install(
273277
# DIRECTORY param
@@ -504,15 +508,56 @@ endif()
504508
##########
505509

506510
if(BUILD_PYTHON)
507-
find_package(PythonInterp 3)
508-
find_package(PythonLibs 3 REQUIRED)
509-
add_library(pyPhotosynthesis MODULE ${SOURCES})
510-
include_directories(${PYTHON_INCLUDE_DIRS})
511-
find_package(Boost 1.36.0 REQUIRED COMPONENTS regex python${PYTHON_VERSION_MAJOR}${PYTHON_VERSION_MINOR})
512-
target_link_libraries(pyPhotosynthesis boost_python${PYTHON_VERSION_MAJOR}${PYTHON_VERSION_MINOR})
513-
target_link_libraries(pyPhotosynthesis boost_regex)
514-
target_link_libraries(pyPhotosynthesis ${PYTHON_LIBRARIES})
515-
target_link_libraries(pyPhotosynthesis ${SUNDIALS_LIBRARIES})
516-
target_compile_options(pyPhotosynthesis PRIVATE -DBUILD_PYTHON)
517-
set_target_properties(pyPhotosynthesis PROPERTIES PREFIX "")
511+
set(PYTHON_PACKAGE_NAME ePhotosynthesis)
512+
set(PYTHON_LIBRARY_NAME pyPhotosynthesis)
513+
find_package(
514+
Python REQUIRED COMPONENTS
515+
Interpreter Development.Module
516+
)
517+
find_package(
518+
Boost 1.36.0 REQUIRED COMPONENTS
519+
regex python${Python_VERSION_MAJOR}${Python_VERSION_MINOR}
520+
)
521+
add_library(${PYTHON_LIBRARY_NAME} MODULE ${SOURCES})
522+
target_link_libraries(
523+
${PYTHON_LIBRARY_NAME}
524+
${Boost_LIBRARIES}
525+
Python::Module
526+
${SUNDIALS_LIBRARIES}
527+
)
528+
target_include_directories(
529+
${PYTHON_LIBRARY_NAME} PUBLIC
530+
${Boost_INCLUDE_DIRS}
531+
${Python_INCLUDE_DIRS}
532+
${SUNDIALS_INCLUDE_DIRS}
533+
$<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}/include>
534+
$<BUILD_INTERFACE:${CMAKE_CURRENT_BINARY_DIR}>
535+
$<INSTALL_INTERFACE:include/ePhotosynthesis>
536+
)
537+
target_compile_options(
538+
${PYTHON_LIBRARY_NAME} PRIVATE -DBUILD_PYTHON
539+
-DPYTHON_LIBRARY_NAME=${PYTHON_LIBRARY_NAME}
540+
)
541+
set_target_properties(${PYTHON_LIBRARY_NAME} PROPERTIES PREFIX "")
542+
543+
# Tests (requires the Python package actually be installed)
544+
add_test(NAME python_tests
545+
COMMAND pytest -svx ${PROJECT_SOURCE_DIR}/tests/python)
546+
547+
# Installation
548+
cmake_path(
549+
APPEND Python_SITEARCH ${PYTHON_PACKAGE_NAME}
550+
OUTPUT_VARIABLE EPHOTOSYNTHESIS_INSTALL_PYTHONDIR
551+
)
552+
install(
553+
TARGETS ${PYTHON_LIBRARY_NAME}
554+
DESTINATION "${EPHOTOSYNTHESIS_INSTALL_PYTHONDIR}"
555+
COMPONENT Python
556+
)
557+
configure_file(cmake/__init__.py.in __init__.py @ONLY)
558+
install(
559+
FILES ${CMAKE_CURRENT_BINARY_DIR}/__init__.py
560+
DESTINATION "${EPHOTOSYNTHESIS_INSTALL_PYTHONDIR}"
561+
COMPONENT Python
562+
)
518563
endif()

bin/ePhotosynthesis.cpp

Lines changed: 14 additions & 76 deletions
Original file line numberDiff line numberDiff line change
@@ -55,29 +55,6 @@ using namespace ePhotosynthesis;
5555
if (result.count(#x) == 0 && inputs.count(#x) > 0) \
5656
x = inputs.at(#x);
5757

58-
#define displayInputVar(fmt, src, val) \
59-
printf("Input variable \"%s\" = " #fmt "\n", #src, val)
60-
61-
#define assignInputVarD(src, dst) \
62-
if (inputs.count(#src) > 0) { \
63-
theVars-> dst = static_cast<double>(stof(inputs.at(#src), nullptr)); \
64-
displayInputVar(%lf, src, theVars-> dst); \
65-
} else \
66-
printf("Input variable \"%s\" not set\n", #src)
67-
#define assignInputVarI(src, dst) \
68-
if (inputs.count(#src) > 0) { \
69-
theVars-> dst = stoi(inputs.at(#src), nullptr); \
70-
displayInputVar(%d, src, theVars-> dst); \
71-
} else \
72-
printf("Input variable \"%s\" not set\n", #src)
73-
74-
#define setInputVarB(src, mod, dst) \
75-
if (inputs.count(#src) > 0) { \
76-
modules::mod::set ## dst ((bool)(stoi(inputs.at(#src), nullptr))); \
77-
displayInputVar(%d, src, stoi(inputs.at(#src), nullptr)); \
78-
} else \
79-
printf("Input variable \"%s\" not set\n", #src)
80-
8158

8259
enum DriverType {
8360
None,
@@ -125,9 +102,9 @@ int main(int argc, const char* argv[]) {
125102
("c,c3", "Use the C3 model, automatically set to true for EPS driver", cxxopts::value<bool>(useC3)->default_value("false"))
126103
("rubiscomethod",
127104
"The method to use for rubisco calculations. Choices are:\n"
128-
"1 - (default) Use enzyme concentration for\n"
105+
"1 - Use enzyme concentration for\n"
129106
" calculation\n"
130-
"2 - Use the michaelis menton and enzyme\n"
107+
"2 - (default) Use the michaelis menton and enzyme\n"
131108
" concentration together for calculation",
132109
cxxopts::value<int>(RUBISCOMETHOD))
133110
("t,abstol", "Absolute tolerance for calculations", cxxopts::value<double>(abstol)->default_value("1e-5"))
@@ -171,68 +148,29 @@ int main(int argc, const char* argv[]) {
171148
if (driverChoice == EPS)
172149
useC3 = true;
173150

151+
Variables *theVars = new Variables();
152+
// Ensure that command line argument takes precedence
153+
if (result.count("Tp") != 0) {
154+
theVars->Tp = Tp;
155+
inputs["Tp_SET"] = std::to_string(Tp);
156+
}
174157
if (fileProvided(evn, evn))
175-
readFile(evn, inputs);
158+
theVars->readParam(evn, inputs);
176159
if (fileProvided(atpcost, atpcost))
177-
readFile(atpcost, inputs);
178-
Variables *theVars = new Variables();
160+
theVars->readParam(atpcost, inputs);
179161
if (fileProvided(enzyme, enzymeFile)) {
180-
std::cerr << "ENZYME DATA PROVIDED" << std::endl;
181-
readFile(enzymeFile, theVars->EnzymeAct, true);
162+
theVars->readEnzymeAct(enzymeFile);
182163
} else {
183164
if (useC3)
184165
throw std::runtime_error("Enzyme data required if --c3 set (automatically true for EPS driver)");
185166
}
186167

187-
assignInputVarD(CO2, CO2_in);
188-
assignInputVarD(PAR, TestLi);
189-
assignInputVarD(ATPCost, TestATPCost);
190-
if (result.count("Tp") == 0) {
191-
assignInputVarD(WeatherTemperature, Tp);
192-
Tp = theVars->Tp;
193-
}
194-
assignInputVarI(GRNC, GRNC);
195-
setInputVarB(SucPath, CM, TestSucPath);
196-
197168
// Read the GRN data and assign it into the correct positions
198169
// based on the expected order
199-
if (fileProvided(grn, grnFile)) {
200-
std::cerr << "GRN DATA PROVIDED" << std::endl;
201-
std::map<std::string, double> glymaID_map;
202-
readFile(grnFile, glymaID_map);
203-
double pcfactor = 1.0 / 0.973;
204-
if (inputs.count("ProteinTotalRatio") > 0)
205-
pcfactor = 1.0 / static_cast<double>(stof(inputs.at("ProteinTotalRatio"), nullptr));
206-
size_t i = 0;
207-
for (auto it = glymaID_order.begin(); it != glymaID_order.end(); it ++, i++) {
208-
double iVfactor = pcfactor;
209-
try {
210-
if ((i >= 33) && (theVars->GRNC == 0))
211-
iVfactor = 1.0;
212-
else
213-
iVfactor = pcfactor * glymaID_map.at(*it);
214-
} catch (const std::out_of_range&) {
215-
// Do nothing
216-
printf("GlymaID \"%s\" not present.\n", it->c_str());
217-
}
218-
if (i < 33) {
219-
theVars->VfactorCp[i] = iVfactor;
220-
} else if (i == 33) {
221-
modules::BF::setcATPsyn(iVfactor);
222-
} else if (i == 34) {
223-
modules::BF::setCPSi(iVfactor);
224-
} else if (i == 35) {
225-
modules::FI::setcpsii(iVfactor);
226-
} else if (i == 36) {
227-
modules::BF::setcNADPHsyn(iVfactor);
228-
} else {
229-
printf("More GlymaIDs than expected.\n");
230-
exit(EXIT_FAILURE);
231-
}
232-
}
233-
}
170+
if (fileProvided(grn, grnFile))
171+
theVars->readGRN(grnFile);
234172

235-
theVars->Tp = Tp;
173+
Tp = theVars->Tp;
236174
theVars->record = record;
237175
theVars->useC3 = useC3;
238176
theVars->RUBISCOMETHOD = RUBISCOMETHOD;

cmake/__init__.py.in

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
from .pyPhotosynthesis import Variables, modules, drivers

environment.yml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,4 +10,5 @@ dependencies:
1010
- pytest
1111
- python
1212
- sundials
13+
- numpy
1314

0 commit comments

Comments
 (0)