Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Make SANSND plugin ready for ncrystal v4 #1 #6

Merged
merged 7 commits into from
Jan 29, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions .github/dependabot.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
version: 2
updates:
- package-ecosystem: "github-actions"
directory: "/"
schedule:
interval: "weekly"
50 changes: 50 additions & 0 deletions .github/workflows/test.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
name: test

on:
push:
pull_request:
schedule:
- cron: '8 15 * * 0' # 8:15 every Monday

jobs:
build_and_test_plugin:
strategy:
matrix:
os: [ ubuntu-latest, macos-latest, windows-latest ]

name: ${{ matrix.os }}
runs-on: ${{ matrix.os }}

steps:
- name: Checkout
uses: actions/checkout@v4
with:
path: src

- name: Install dependencies
run: pip install 'ncrystal-core>=3.9.86' "scikit-build-core>=0.10" "ncrystal-pypluginmgr>=0.0.3" "ncrystal-python>=3.9.86"

- name: Install plugin
run: pip install ./src

- name: Verify plugin loading
shell: python
run: |
import os
os.environ['NCRYSTAL_PLUGIN_RUNTESTS'] = '1'
os.environ['NCRYSTAL_REQUIRED_PLUGINS'] = 'SANSND'
import NCrystal
NCrystal.browsePlugins(dump=True)

- name: Use plugin file
run: nctool -d 'plugins::SANSND/ncplugin-SANSND_nanodiamond.ncmat'

- name: Load all plugin files
shell: python
run: |
from NCrystal.datasrc import browseFiles
from NCrystal.core import createScatter
for f in browseFiles(factory='plugins'):
if f.name.startswith('SANSND/'):
print('Loading f.fullKey')
createScatter( f'{f.fullKey}' )
28 changes: 3 additions & 25 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -7,8 +7,7 @@
#Also needed for vi:
.*.sw?

#ignore tempory cmake build output and installations:
/testcode/utils/cache/
/venv/

#Extra protection against adding in build output created by custom user commands:
*.so
Expand All @@ -29,30 +28,9 @@
*.app

__pycache__/
build/

testcode/data/njoy_calc.txt
testcode/data/teshi.dat
testcode/data/vesuvio_unc.dat
testcode/data/vesuvio.dat
testcode/data/vesuvio_4mm.inp
testcode/scripts/plotradicand.py

.ipynb_checkpoints/
.vscode/

#Miscellaneous:
.DS_Store
vgcore.*
.vscode/
testcode/data/openmc_fwd.dat
testcode/data/nesvi.dat
testcode/data/openmc_bkw.dat
testcode/data/F4690504.tmp:Zone.Identifier
testcode/data/AA6BE253.tmp:Zone.Identifier
testcode/data/9CC7D60B.tmp:Zone.Identifier
testcode/data/3DCFA2D9.tmp:Zone.Identifier
boost
testcode/data/Ersez_dataset:Zone.Identifier
testcode/data/*:Zone.Identifier
testcode/data/muvals
testcode/data/muvals_files
compile_plugin.sh
198 changes: 54 additions & 144 deletions CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -1,167 +1,77 @@

# CMake code for building the plugin and possible also testcode in the testcode/
# subdirectory. In general, please try to refrain from editing this file!
# Anything special that needs to be done to support the plugin development
# environment can be done in testcode/CMakeLists.txt, so it won't affect the main plugin.

cmake_minimum_required(VERSION 3.10...3.19)

execute_process( COMMAND ncrystal-config --show cmakedir
OUTPUT_VARIABLE NCrystal_DIR
OUTPUT_STRIP_TRAILING_WHITESPACE )
find_package(NCrystal REQUIRED)
cmake_minimum_required(VERSION 3.20...3.30)

#Extract plugin name from ncplugin_name.txt:
file(STRINGS "${CMAKE_CURRENT_LIST_DIR}/ncplugin_name.txt" NCPlugin_NAME LIMIT_COUNT 1)
file( STRINGS "${CMAKE_CURRENT_LIST_DIR}/ncplugin_name.txt" NCPlugin_NAME LIMIT_COUNT 1)
string(STRIP "${NCPlugin_NAME}" NCPlugin_NAME)

project( "NCPlugin_${NCPlugin_NAME}" VERSION 0.0.1 LANGUAGES CXX)

#Project has two options. While developing the project, one should use the default values.

#1) NCPLUGIN_DEVMODE: enables the subproject in testcode/ and enables various
# strict compilation options, which helps to ensure that the plugin will
# contain code appropriate for NCrystal.
#2) NCPLUGIN_ASBUILTIN: This option is used when the plugin code should be
# embedded directly into the primary NCrystal library.

option(NCPLUGIN_DEVMODE "Enable strict compilation flags and build test code" ON)
option(NCPLUGIN_ASBUILTIN "Do not build plugin, just prepare CMake variables for static inclusion (variable used by NCrystal's CMake code)" OFF)

if (NCPLUGIN_DEVMODE AND NCPLUGIN_ASBUILTIN)
message(FATAL_ERROR "The options NCPLUGIN_ASBUILTIN and NCPLUGIN_DEVMODE can not be enabled simultaneously")
endif()
set( ncplugin_data_file_pattern "data/*.ncmat" )

#Ensure we have a default build type if not already specified:
if ( NOT DEFINED CMAKE_BUILD_TYPE )
if (NCPLUGIN_DEVMODE)
set( CMAKE_BUILD_TYPE Debug )
else()
set( CMAKE_BUILD_TYPE RelWithDebInfo )
if ( DEFINED SKBUILD_PROJECT_NAME )
if ( NOT "${SKBUILD_PROJECT_NAME}" STREQUAL "ncrystal_plugin_${NCPlugin_NAME}" )
message(
FATAL_ERROR "Mismatch in plugin name hardcoded in "
" ncplugin_name.txt and pyproject.toml's project.name field."
)
endif()
endif()

#Plugin C++ files:
function(srcfileglob varname pattern)
#Glob with CONFIGURE_DEPENDS + ignoring temporary files left around by editors.
file( GLOB tmpall LIST_DIRECTORIES false CONFIGURE_DEPENDS "${PROJECT_SOURCE_DIR}/${pattern}" )
#Must always build against NCrystal:
if( NOT DEFINED "NCrystal_DIR" )
#Need to invoke "ncrystal-config --show cmakedir" if we want to be able to
#work with ncrystal-core installed via python wheels:
execute_process(
COMMAND ncrystal-config --show cmakedir
OUTPUT_VARIABLE "NCrystal_DIR" OUTPUT_STRIP_TRAILING_WHITESPACE
)
endif()
find_package( NCrystal 3.9.85 REQUIRED )

function( ncrystal_srcfileglob varname pattern )
#Glob while ignoring temporary files.
file(
GLOB tmpall LIST_DIRECTORIES false
CONFIGURE_DEPENDS "${pattern}"
)
set(tmp "")
foreach(fn ${tmpall})
get_filename_component( bn "${fn}" NAME)
if (bn MATCHES "(#|~| )+")#could ignore . as well for scripts "|\\."
message("----> Ignoring file with invalid name: ${bn}")
if (bn MATCHES "(#|~| )+")
message( WARNING "Ignoring file with invalid name: ${bn}")
else()
list(APPEND tmp "${fn}")
endif()
endforeach()
set( ${varname} ${tmp} PARENT_SCOPE )
endfunction()

#Find files (also has the effect of triggering auto-reconf if the glob results change):
srcfileglob( plugin_srcfiles "src/*.cc" )
srcfileglob( plugin_hdrfiles "include/*.hh" )
srcfileglob( plugin_hdrfiles_icc "include/*.icc" )
srcfileglob( plugin_datafiles "data/*.ncmat" )
list(APPEND plugin_hdrfiles ${plugin_hdrfiles_icc})
srcfileglob( dummy "src/*.hh" )#To trigger reconf
srcfileglob( dummy "src/*.icc" )#To trigger reconf

if (NCPLUGIN_ASBUILTIN)
#Special mode which does almost nothing.
#NCrystal's main cmake code will build the plugin code into the primary
#NCrystal library. For that to work, we need to ensure that the files from the
#current plugin get the correct compile definitions:
get_directory_property(hasParent PARENT_DIRECTORY)
if (NOT hasParent)
message(FATAL_ERROR "NCPLUGIN_ASBUILTIN option can only be used when the plugin project is a sub-project")
endif()
set(NCPLUGIN_SRCFILES ${plugin_srcfiles} PARENT_SCOPE)#<--- pass up variable to parent project
set(NCPLUGIN_DATAFILES ${plugin_datafiles} PARENT_SCOPE)#<--- pass up variable to parent project
set(NCPLUGIN_COMPILEDEFS NCPLUGIN_NAME=${NCPlugin_NAME} NCPLUGIN_ASBUILTIN PARENT_SCOPE)
set(NCPLUGIN_INCDIRS ${PROJECT_SOURCE_DIR}/include PARENT_SCOPE)
return()
endif()

#Standard build, need NCrystal as dependency:

find_package(NCrystal 3.0.0 REQUIRED)

if ( NCPLUGIN_DEVMODE )
#Compile with very strict C++11 options during normal development (this gives
#better quality code, and ensures that the code developed is better suited for
#possible inclusion in NCrystal at some point):
set(CMAKE_CXX_STANDARD 11)
set(CMAKE_CXX_STANDARD_REQUIRED ON)
set(CMAKE_CXX_EXTENSIONS OFF)
set(CMAKE_CXX_COMPILE_FLAGS ${CMAKE_CXX_COMPILE_FLAGS} -Wall -Wextra -pedantic -Werror )
#Limit how many errors are dumped on the poor developer:
include(CheckCXXCompilerFlag)
check_cxx_compiler_flag( -fmax-errors=3 COMPILER_SUPPORTS_MAXERRORS )
if ( COMPILER_SUPPORTS_MAXERRORS )
set(CMAKE_CXX_COMPILE_FLAGS ${CMAKE_CXX_COMPILE_FLAGS} -fmax-errors=3 )
endif()
#Try to make life easier by adding rpaths (to ncrystal lib):
if ( NOT DEFINED CMAKE_INSTALL_RPATH_USE_LINK_PATH )
set( CMAKE_INSTALL_RPATH_USE_LINK_PATH TRUE )
endif()
endif()

#The actual plugin should be build as a shared library. In case it is ever
#needed, we export everything for possible downstream usage: header files,
#targets, and cmake cfg files:
add_library( pluginlib SHARED ${plugin_srcfiles} )
target_compile_definitions( pluginlib PRIVATE NCPLUGIN_NAME=${NCPlugin_NAME} )
set_target_properties( pluginlib PROPERTIES OUTPUT_NAME ${PROJECT_NAME} )
target_compile_definitions( pluginlib PRIVATE NCRYSTAL_NO_CMATH_CONSTANTS )
target_link_libraries( pluginlib PUBLIC NCrystal::NCrystal )
target_include_directories( pluginlib PRIVATE "${PROJECT_SOURCE_DIR}/src"
PUBLIC $<BUILD_INTERFACE:${PROJECT_SOURCE_DIR}/include>
$<INSTALL_INTERFACE:include/${PROJECT_NAME}> )
install( TARGETS pluginlib EXPORT ${PROJECT_NAME}Targets DESTINATION lib )
install( FILES ${plugin_hdrfiles} DESTINATION include/${PROJECT_NAME} )
install( EXPORT ${PROJECT_NAME}Targets FILE "${PROJECT_NAME}Targets.cmake" NAMESPACE "${PROJECT_NAME}::" DESTINATION lib/cmake )
add_library("${PROJECT_NAME}::pluginlib" ALIAS pluginlib)
include(CMakePackageConfigHelpers)
write_basic_package_version_file( "${PROJECT_BINARY_DIR}/${PROJECT_NAME}ConfigVersion.cmake"
VERSION ${PROJECT_VERSION} COMPATIBILITY AnyNewerVersion )
configure_file( "${PROJECT_SOURCE_DIR}/PkgConfig.cmake.in"
"${PROJECT_BINARY_DIR}/${PROJECT_NAME}Config.cmake" @ONLY )
install( FILES "${PROJECT_BINARY_DIR}/${PROJECT_NAME}ConfigVersion.cmake"
"${PROJECT_BINARY_DIR}/${PROJECT_NAME}Config.cmake" DESTINATION lib/cmake )

#For simplicity, any data files are embedded directly in the library (except in
#NCPLUGIN_DEVMODE where the testcode project will make them available via
#symlinks):

if (plugin_datafiles)
#In any mode we sanity check the names of the data files (so developers will
#notice and fix any issues):
foreach(df ${plugin_datafiles})
get_filename_component(dfbn "${df}" NAME)
if(NOT dfbn MATCHES "ncplugin-${NCPlugin_NAME}_.+\.ncmat")
message(FATAL_ERROR "ERROR: name of exported datafile ${dfbn} does not have required form: ncplugin-${NCPlugin_NAME}_*.ncmat")
endif()
endforeach()
if (NOT NCPLUGIN_DEVMODE)
#Check that data file name follows convention: ncplugin-<pluginname>_*.ncmat:
if (NOT NCrystal_CMD_NCMAT2CPP)
message(FATAL_ERROR "ERROR: NCrystal installation does not provide ncrystal_ncmat2cpp command which is needed to embed datafiles in plugin.")
endif()
#Generate C++ code from the .ncmat files:
execute_process(COMMAND "${NCrystal_CMD_NCMAT2CPP}"
"-n" "NCPluginNamespace::registerDataFiles"
--include NCrystal/NCPluginBoilerplate.hh
"-o" "${PROJECT_BINARY_DIR}/autogen_${NCPlugin_NAME}_ncmat_data.cc" ${plugin_datafiles} RESULT_VARIABLE status )
if(status AND NOT status EQUAL 0)
message(FATAL_ERROR "Failure while trying to invoke ncrystal_ncmat2cpp (from ${NCrystal_CMD_NCMAT2CPP}).")
endif()
target_sources(pluginlib PRIVATE "${PROJECT_BINARY_DIR}/autogen_${NCPlugin_NAME}_ncmat_data.cc")#too late to just append to plugin_srcfiles
target_compile_definitions(pluginlib PRIVATE NCPLUGIN_DO_REGISTERDATAFILES)
message("-- Generated autogen_${NCPlugin_NAME}_ncmat_data.cc with embedded NCMAT data (will be compiled into the plugin library).")
endif()
ncrystal_srcfileglob( plugin_srcfiles "${PROJECT_SOURCE_DIR}/src/*.cc" )
ncrystal_srcfileglob( plugin_hdrfiles "${PROJECT_SOURCE_DIR}/src/*.hh" )
set( pluglib "NCPlugin_${NCPlugin_NAME}" )
add_library( ${pluglib} MODULE ${plugin_srcfiles} )
set_source_files_properties( ${plugin_srcfiles} PROPERTIES OBJECT_DEPENDS "${plugin_hdrfiles}" )
target_compile_definitions( ${pluglib} PRIVATE "NCPLUGIN_NAME=${NCPlugin_NAME}" "NCRYSTAL_NO_CMATH_CONSTANTS" )
target_link_libraries( ${pluglib} PRIVATE NCrystal::NCrystal )
target_include_directories( ${pluglib} PRIVATE "${PROJECT_SOURCE_DIR}/src" )

if ( ncplugin_data_file_pattern )
file(GLOB plugin_datafiles LIST_DIRECTORIES false CONFIGURE_DEPENDS
"${PROJECT_SOURCE_DIR}/${ncplugin_data_file_pattern}" )
else()
set( plugin_datafiles "" )
endif()

if ( NCPLUGIN_DEVMODE )
#Development code:
add_subdirectory(testcode)
if ( DEFINED SKBUILD_PROJECT_NAME )
#Install in wheel platlib dir:
set( pymoddir "${SKBUILD_PLATLIB_DIR}/ncrystal_plugin_${NCPlugin_NAME}")
install( TARGETS ${pluglib} LIBRARY DESTINATION "${pymoddir}/plugins" )
#Also add an empty __init__.py:
file( TOUCH "${PROJECT_BINARY_DIR}/__init__.py" )
INSTALL( FILES "${PROJECT_BINARY_DIR}/__init__.py" DESTINATION "${pymoddir}" )
#Special location for data files:
install( FILES ${plugin_datafiles} DESTINATION "${pymoddir}/data" )
else()
install( TARGETS ${pluglib} LIBRARY DESTINATION lib )
install( FILES ${plugin_datafiles} DESTINATION data )
endif()
4 changes: 0 additions & 4 deletions PkgConfig.cmake.in

This file was deleted.

Loading