The code.ill.fr has been recreated and upgraded with the latest version this weekend, If you encounter any problem please inform the Helpdesk.

Commit 64e93fbf authored by juan rodriguez-carvajal's avatar juan rodriguez-carvajal
Browse files
parents 647ec3c2 c47f28f4
Pipeline #10565 passed with stages
in 13 minutes and 6 seconds
name: Python libs
on:
push:
branches: [ master ]
pull_request:
branches: [ master ]
# schedule:
# # Every day at 1am
# - cron: '0 1 * * *'
jobs:
get_Latest:
runs-on: ubuntu-latest
if: "!contains(github.event.head_commit.message, '[ci skip]')"
steps:
- name: Checkout ILL repo
run: |
git clone https://code.ill.fr/scientific-software/crysfml/ ./
git checkout feature-python_api-build_wheels
rm -rf ./.git ./Html
- uses: actions/upload-artifact@v2
with:
name: CrysFML_SRC
path: ${{ github.workspace }}
retention-days: 1
create-Linux:
runs-on: ubuntu-latest
needs: get_Latest
steps:
- uses: actions/download-artifact@v2
with:
name: CrysFML_SRC
- name: Build manylinux wheels
run: |
chmod +x ./Scripts/buildscript.sh
docker run -e PLAT=manylinux2010_x86_64 -v ${{ github.workspace }}:/io quay.io/pypa/manylinux2010_x86_64 /io/Scripts/buildscript.sh
- uses: actions/upload-artifact@v2
with:
name: CrysFML - Linux
path: ${{ github.workspace }}/dist/*.whl
create-OSX:
strategy:
max-parallel: 4
matrix:
python-version: ['3.8']
os: [macos-10.15]
gcc_v: [ 10 ] # Version of GFortran we want to use.
runs-on: ${{ matrix.os }}
needs: get_Latest
steps:
- uses: actions/download-artifact@v2
with:
name: CrysFML_SRC
- name: Setup python
uses: actions/setup-python@v1
with:
python-version: ${{matrix.python-version}}
- name: Install cmake
run: pip3 install cmake delocate wheel
- name: Build CFML (OSX)
env:
FC: gfortran-${{ matrix.gcc_v }}
CC: gcc-${{ matrix.gcc_v }}
run: |
pip3 wheel ./ --no-deps -w ./old/
delocate-wheel -w dist -v ./old/*.whl
- uses: actions/upload-artifact@v2
with:
name: CrysFML - MacOS
path: ./dist/*.whl
create-Windows:
strategy:
max-parallel: 1
matrix:
python-version: [ '3.6', '3.7']
os: [ windows-latest ]
runs-on: ${{ matrix.os }}
steps:
- uses: actions/checkout@v2
- uses: conda-incubator/setup-miniconda@v2
with:
auto-update-conda: true
python-version: ${{ matrix.python-version }}
auto-activate-base: true
- uses: msys2/setup-msys2@v2
with:
msystem: MSYS
install: cmake mingw-w64-x86_64-toolchain
update: true
path-type: inherit
- name: Build CFML
shell: msys2 {0}
id: build
run: python setup.py bdist_wheel
env:
MINGW_ARCH: MINGW64
- uses: actions/upload-artifact@v2
with:
name: CrysFML - Windows
path: ./dist/*.whl
Upload_Artifacts:
runs-on: ubuntu-latest
needs: [create-Linux, create-OSX]
steps:
- uses: actions/download-artifact@v2
- name: What is downloaded
run: |
ls -al
- name: Upload Artifacts GitHub releases
uses: ncipollo/release-action@v1
with:
draft: false
prerelease: true
allowUpdates: true
replacesArtifacts: true
token: ${{ secrets.GITHUB_TOKEN }}
artifacts: "./**/*.whl"
tag: "CurrentBuild"
body: "This is a build of the CFML libraries."
......@@ -37,3 +37,12 @@
# Directories
Build
.idea/
build/
CFML.egg-info/
dist/
*.whl
......@@ -14,6 +14,7 @@ option(HEAP_ARRAYS "Put arrays in heap instead of stack (only for Windows ifort)
option(PROG_EX "Build the Program Examples" ON)
option(PYTHON_API "Build Python API" OFF)
option(QPARALLEL "qparallel option for Ifort on Windows" ON)
option(PYSETUP "Building from setup.py" OFF)
if(DEFINED CMAKE_Fortran_COMPILER)
set(CMAKE_Fortran_COMPILER ${CMAKE_Fortran_COMPILER} CACHE STRING "The Fortran compiler. One of 'ifort', 'g95' or 'gfortran'.")
......@@ -52,16 +53,34 @@ message(STATUS "CrysFML installation path set to ${CRYSFML_PREFIX}")
# Include and library paths for Python.
# They must be set by the user.
if(DEFINED PYTHON_LIBRARY_PATH)
set(PYTHON_LIBRARY_PATH ${PYTHON_LIBRARY_PATH} CACHE PATH "Python library path")
else()
set(PYTHON_LIBRARY_PATH "" CACHE PATH "Python library path")
endif()
if(DEFINED PYTHON_INTERPRETER_PATH)
set(PYTHON_INTERPRETER_PATH ${PYTHON_INTERPRETER_PATH} CACHE PATH "Python interpreter path")
else()
set(PYTHON_INTERPRETER_PATH "" CACHE PATH "Python interpreter path")
endif()
# if(DEFINED PYTHON_LIBRARY_PATH)
# set(PYTHON_LIBRARY_PATH ${PYTHON_LIBRARY_PATH} CACHE PATH "Python library path")
# else()
# set(PYTHON_LIBRARY_PATH "" CACHE PATH "Python library path")
# endif()
# if(DEFINED PYTHON_INTERPRETER_PATH)
# set(PYTHON_INTERPRETER_PATH ${PYTHON_INTERPRETER_PATH} CACHE PATH "Python interpreter path")
# else()
# set(PYTHON_INTERPRETER_PATH "" CACHE PATH "Python interpreter path")
# endif()
# Use CMake v3.12+ FindPython3 to find the system-compatible interpreter *and* libraries at once
# This solves a potential issue on convoluted Windows build systems which have multiple
# python interpreters, some of which do not have build-system compatibility (I'm looking at you, msys2 python)
# In such a case FindPythonInterp might point to the wrong python interpreter and then further configuration
# steps will fail to find any (or any compatible) libraries which match the interpreter.
# find_package(Python3 COMPONENTS Interpreter Development)
# # Since FindPython3 and FindPythonInterp set different variables, fake the FindPythonInterp result
# # to prevent it running if called by, e.g., FindPybind11
# if(Python3_FOUND)
# set(PYTHONINTERP_FOUND ON)
# set(PYTHON_EXECUTABLE ${Python3_EXECUTABLE})
# message(STATUS "USING PYTHON: " ${PYTHON_EXECUTABLE})
# # set(PYTHON_INTERPRETER_PATH ${Python3_EXECUTABLE})
# set(PYTHON_VERSION_STRING "${Python3_VERSION}")
# set(PYTHON_VERSION_MAJOR ${Python3_VERSION_MAJOR})
# set(PYTHON_VERSION_MINOR ${Python3_VERSION_MINOR})
# set(PYTHON_VERSION_PATCH ${Python3_VERSION_PATCH})
# endif()
# Include and library paths for HDF5.
# They must be set by the user.
......
......@@ -20,7 +20,7 @@ target_link_libraries(read_nexus crysfml)
link_directories(${HDF5_LIBRARY_DIR})
target_link_libraries(read_nexus ${HDF5_LIBRARIES})
if(WIN32)
if(WIN32 OR MSYS)
get_filename_component(COMPILER_NAME ${CMAKE_Fortran_COMPILER} NAME_WE)
if(COMPILER_NAME STREQUAL ifort)
# Copy hdf5.dll hdf5_f90cstub.dll hdf5_fortran.dll
......
......@@ -42,7 +42,7 @@ class Atom(CFML_api.FortranBindedClass):
def __str__(self):
xyz = self.xyz
return 'ATOM %s %s %s %s %s %s %s'%(self.label, self.chemical_symbol, str(xyz[0]), str(xyz[1]), str(xyz[2]), self.biso, self.site_multiplicity)
return 'ATOM %s %s %s %s %s %s %s %s %s'%(self.label, self.chemical_symbol, str(xyz[0]), str(xyz[1]), str(xyz[2]), str(self.biso), str(self.occ), str(self.moment), str(self.formal_charge))
@property
def label(self):
......@@ -343,7 +343,12 @@ class AtomList(CFML_api.FortranBindedClass):
"""
def __init__(self, string_array=None):
CFML_api.FortranBindedClass.__init__(self)
self._mult = True
self._adp = True
if string_array:
self._mult = False
self._adp = False
dict = CFML_api.crysfml_api.atom_typedef_atomlist_from_CIF_string_array(string_array)
self._set_fortran_address(dict["AtomList"])
......@@ -366,7 +371,7 @@ class AtomList(CFML_api.FortranBindedClass):
@property
def natoms(self):
"""
total number of atoms in the list
Total number of atoms in the list
"""
return CFML_api.crysfml_api.atom_typedef_get_natoms(self.get_fortran_address())["natoms"]
......@@ -374,3 +379,29 @@ class AtomList(CFML_api.FortranBindedClass):
""" Print the list of atoms. """
CFML_api.crysfml_api.atom_typedef_write_atom_list(self.get_fortran_address())
def set_mult_occ_cif(self,spg):
"""
Update the multiplicity and modify the occupancy when reading from a CIF array,
in order to be in agreement with the definitions of Sfac in CrysFML
This needs to be run when the AtomList is initialised from a CIF-like stringarray
rather than from the class CIFFile
"""
if (self._mult == False):
CFML_api.crysfml_api.atom_typedef_atomlist_reset_occ_cif(self.get_fortran_address(),spg.get_fortran_address())
self._mult = True
else:
print("Warning: multiplicity and occupancy have already been set")
def set_all_anisotropic_displacement_parameters_cif(self,cell):
"""
Update all Atom fields for Us, Bs and betas
This needs to be run when the AtomList is initialised from a CIF-like stringarray
rather than from the class CIFFile
"""
if (self._adp == False):
CFML_api.crysfml_api.atom_typedef_atomlist_set_all_adp_cif(self.get_fortran_address(),cell.get_fortran_address())
self._adp = True
else:
print("Warning: Anisotropic Displacement Parameters have already been set")
......@@ -170,3 +170,72 @@ class Cell(CFML_api.FortranBindedClass):
return CFML_api.crysfml_api.crystal_metrics_get_CartType(self.get_fortran_address())["CartType"]
def k_to_cart_vector(code, vector, cell):
"""
Convert a vector in crystal space to cartesian components
The value of code has been extended to use also the Busing-Levy
Cartesian system as reference also for direct and reciprocal space.
Codes:
The Cartesian frame is that defined by the setting of the Cell object
D: The components are given with respect to basis (a,b,c)
R: The components are given with respect to basis (a*,b*,c*)
BL: The components are given with respect to basis (a*,b*,c*) but
the Cartesian frame is that defined by Busing and Levy
BLD: The components are given with respect to basis (a,b,c) but
the Cartesian frame is that defined by Busing and Levy
...
Parameters
----------
code : str
Cartesian frame
vector : np.array[dtype='float32', ndim=1]
kx,ky,kz coordinates
cell : CMFL_api.crysfml_api.Cell
Returns:
np.array[dtype='float32', ndim=1] : coordinates in the chosen cartesian frame
"""
return CFML_api.crysfml_api.crystal_metrics_cart_vector(code, vector, cell.get_fortran_address())["cart_vec"]
def k_to_cart_unitary_vector(code, vector, cell):
"""
Convert a vector in crystal space to unitary cartesian components
The value of code has been extended to use also the Busing-Levy
Cartesian system as reference also for direct and reciprocal space.
Codes:
The Cartesian frame is that defined by the setting of the Cell object
D: The components are given with respect to basis (a,b,c)
R: The components are given with respect to basis (a*,b*,c*)
BL: The components are given with respect to basis (a*,b*,c*) but
the Cartesian frame is that defined by Busing and Levy
BLD: The components are given with respect to basis (a,b,c) but
the Cartesian frame is that defined by Busing and Levy
...
Parameters
----------
code : str
Cartesian frame
vector : np.array[dtype='float32', ndim=1]
kx,ky,kz coordinates
cell : CMFL_api.crysfml_api.Cell
Returns:
np.array[dtype='float32', ndim=1] : coordinates in the chosen cartesian frame
"""
return CFML_api.crysfml_api.crystal_metrics_cart_u_vector(code, vector, cell.get_fortran_address())["cart_vec"]
def betas_from_biso(biso, cell):
"""
Get Betas from Biso
...
Parameters
----------
biso : float32
cell : CMFL_api.crysfml_api.Cell
Returns
np.array[dtype='float32', ndim=1] : betas[0:5] b11, b22, b33, b12, b13, b23
"""
return CFML_api.crysfml_api.crystal_metrics_get_betas_from_biso(cell.get_fortran_address(),biso)["betas"]
#################################
# Commad line arguments section
#################################
cmake_minimum_required(VERSION 3.1.0 FATAL_ERROR)
cmake_minimum_required(VERSION 3.13 FATAL_ERROR)
#################################
# Project section
......@@ -29,39 +29,38 @@ set_source_files_properties(forpy_mod.f90 PROPERTIES COMPILE_FLAG "-cpp")
# Dependencies section
#################################
# Add a dependency to crysfml_common to keep sure that the crysfml_common library will be built first.
# See https://gitlab.kitware.com/cmake/cmake/-/issues/19226 for linking
add_dependencies(${LIBRARY_NAME} crysfml_common)
set(COMPATIBLE_VERSIONS 3.8 3.7 3.6 3.5)
if (NOT PYTHON_INTERPRETER_PATH)
set(Python_ADDITIONAL_VERSIONS ${COMPATIBLE_VERSIONS})
find_package(PythonInterp REQUIRED)
set(PYTHON_INTERPRETER_PATH ${PYTHON_EXECUTABLE})
message(STATUS ${PYTHON_INTERPRETER_PATH})
else()
message(STATUS "using Python interpreter provided with PYTHON_INTERPRETER_PATH variable: " ${PYTHON_INTERPRETER_PATH})
endif()
if (NOT PYTHON_LIBRARY_PATH)
set(Python_ADDITIONAL_VERSIONS ${COMPATIBLE_VERSIONS})
find_package(PythonLibs REQUIRED)
set(PYTHON_LIBRARY_PATH ${PYTHON_LIBRARY})
message(STATUS ${PYTHON_LIBRARY_PATH})
if (NOT PYTHON_INTERPRETER_PATH OR NOT PYTHON_LIBRARY_PATH)
set(Python3_USE_STATIC_LIBS, TRUE)
find_package(Python3 COMPONENTS Interpreter Development)
set(PYTHON_INTERPRETER_PATH ${Python3_EXECUTABLE})
set(PYTHON_LIBRARY_PATH Python3::Python)
message(STATUS "Using Python interpreter found with PYTHON_INTERPRETER_PATH variable: " ${PYTHON_INTERPRETER_PATH})
message(STATUS "Linking Python extension with found PYTHON_LIBRARY_PATH variable: " ${PYTHON_LIBRARY_PATH})
else()
message(STATUS "Using Python interpreter provided with PYTHON_INTERPRETER_PATH variable: " ${PYTHON_INTERPRETER_PATH})
message(STATUS "Linking Python extension with provided PYTHON_LIBRARY_PATH variable: " ${PYTHON_LIBRARY_PATH})
endif()
if (UNIX)
add_library(Python3_LIB SHARED IMPORTED)
set_property(TARGET Python3_LIB PROPERTY IMPORTED_LOCATION ${PYTHON_LIBRARY_PATH})
elseif(WIN32)
if(WIN32 OR MSYS)
add_library(Python3_LIB STATIC IMPORTED)
set_property(TARGET Python3_LIB PROPERTY IMPORTED_LOCATION ${PYTHON_LIBRARY_PATH})
target_link_libraries(${LIBRARY_NAME} Python3_LIB)
endif()
# Set -Wl,-undefined,dynamic_lookup to OTHER_LDFLAGS
# Set include and linking
include_directories(${CRYSFML_COMMON_MODULE_DIRECTORY})
target_link_libraries(${LIBRARY_NAME} crysfml_common)
target_link_libraries(${LIBRARY_NAME} Python3_LIB)
# Fix a problem on Mac OS X when building shared libraries
if(APPLE)
set(CMAKE_SHARED_LINKER_FLAGS "${CMAKE_SHARED_LINKER_FLAGS} -undefined dynamic_lookup")
endif()
# Set library extension
set_target_properties(${LIBRARY_NAME} PROPERTIES PREFIX "" OUTPUT_NAME ${LIBRARY_NAME})
......@@ -69,7 +68,7 @@ set(LIBRARY_FILENAME "crysfml_api.so")
if(APPLE)
set_target_properties(${LIBRARY_NAME} PROPERTIES SUFFIX ".so")
set(LIBRARY_FILENAME "crysfml_api.so")
elseif(WIN32)
elseif(WIN32 OR MSYS)
set_target_properties(${LIBRARY_NAME} PROPERTIES SUFFIX ".pyd")
set(LIBRARY_FILENAME "crysfml_api.pyd")
endif()
......@@ -112,42 +111,56 @@ endif (DOXYGEN_FOUND)
#################################
# Add tests
add_test(NAME "Python_API_Tests" COMMAND ${PYTHON_INTERPRETER_PATH} -m unittest discover ${CMAKE_CURRENT_BINARY_DIR}/../Tests)
add_custom_target(Python_Files ALL
if(PYSETUP)
add_custom_target(Python_Files ALL
COMMAND
${CMAKE_COMMAND} -E copy_directory
${CMAKE_CURRENT_SOURCE_DIR}/../Tests
${CMAKE_CURRENT_BINARY_DIR}/../Tests
COMMAND
${CMAKE_COMMAND} -E copy_directory
${CMAKE_CURRENT_SOURCE_DIR}
${CMAKE_CURRENT_BINARY_DIR}/../Tests/CFML_api
)
else()
add_custom_target(Python_Files ALL
COMMAND
${CMAKE_COMMAND} -E copy_directory
${CMAKE_CURRENT_SOURCE_DIR}/../Tests
${CMAKE_CURRENT_BINARY_DIR}/../Tests
COMMAND
${CMAKE_COMMAND} -E copy_directory
${CMAKE_CURRENT_SOURCE_DIR}
${CMAKE_CURRENT_BINARY_DIR}/../Tests/CFML_api
COMMAND
${CMAKE_COMMAND} -E copy
${CMAKE_CURRENT_BINARY_DIR}/${LIBRARY_FILENAME}
${CMAKE_CURRENT_BINARY_DIR}/../Tests/CFML_api
${CMAKE_CURRENT_BINARY_DIR}/../Tests/CFML_api/${LIBRARY_FILENAME}
)
add_dependencies(Python_Files ${LIBRARY_NAME})
endif()
add_dependencies(Python_Files ${LIBRARY_NAME})
#################################
# Install section
#################################
# The rules for installing the library.
install(DIRECTORY "" DESTINATION ${PYTHON_API_PREFIX}/CFML_api FILES_MATCHING PATTERN "*.py" PATTERN "Extensions" EXCLUDE)
if(WIN32)
install(TARGETS ${LIBRARY_NAME} RUNTIME DESTINATION ${PYTHON_API_PREFIX}/CFML_api)
if(WIN32 OR MSYS)
install(TARGETS ${LIBRARY_NAME} RUNTIME DESTINATION ${PYTHON_API_PREFIX}/CFML_api)
else()
install(TARGETS ${LIBRARY_NAME} LIBRARY DESTINATION ${PYTHON_API_PREFIX}/CFML_api)
install(TARGETS ${LIBRARY_NAME} LIBRARY DESTINATION ${PYTHON_API_PREFIX}/CFML_api)
endif()
# Installing example files
install(DIRECTORY "${CMAKE_CURRENT_SOURCE_DIR}/../Examples" DESTINATION ${PYTHON_API_PREFIX} FILES_MATCHING PATTERN "*.py")
install(DIRECTORY "${CMAKE_CURRENT_SOURCE_DIR}/../Examples/Data" DESTINATION ${PYTHON_API_PREFIX}/Examples FILES_MATCHING PATTERN "*.cfl" PATTERN "*.cif")
# Installing tests files
#
# # Installing tests files
install(DIRECTORY "${CMAKE_CURRENT_SOURCE_DIR}/../Tests" DESTINATION ${PYTHON_API_PREFIX} FILES_MATCHING PATTERN "*.py")
# Installing documentation files
#
# # Installing documentation files
install(DIRECTORY "${CMAKE_CURRENT_BINARY_DIR}/doxygen/html" DESTINATION ${PYTHON_API_PREFIX}/Doc OPTIONAL)
\ No newline at end of file
......@@ -4,6 +4,8 @@ module API_Atom_TypeDef
use, intrinsic :: iso_c_binding
use, intrinsic :: iso_fortran_env
use CFML_GlobalDeps, only : cp, eps
use CFML_Atom_TypeDef, only: &
Atom_List_type, &
Atoms_Cell_type, &
......@@ -13,9 +15,23 @@ module API_Atom_TypeDef
Write_Atom_list, &
Atom_type
Use CFML_Crystallographic_Symmetry, only: &
Get_Multip_Pos
use CFML_Crystal_Metrics, only: &
Convert_U_Betas, Convert_B_Betas
use CFML_IO_Formats, only: &
Read_Atom, Read_Cif_Atom
use API_Crystallographic_Symmetry, only: &
Space_Group_type_p, &
get_space_group_type_from_arg
use API_Crystal_Metrics, only: &
Crystal_Cell_type_p, &
get_cell_from_arg
implicit none
type Atom_type_p
......@@ -178,6 +194,118 @@ contains
end function atom_typedef_atomlist_from_CIF_string_array
!---- Modify occupation factors and set multiplicity of atoms
!---- in order to be in agreement with the definitions of Sfac in CrysFML
function atom_typedef_atomlist_reset_occ_cif(self_ptr, args_ptr) result(r) bind(c)
type(c_ptr), value :: self_ptr
type(c_ptr), value :: args_ptr
type(c_ptr) :: r
type(tuple) :: args
type(dict) :: retval
integer :: num_args
integer :: ierror
type(Atom_List_Type_p) :: a_p
type(Space_Group_type_p) :: spg
real(kind=cp),dimension(6):: pos
integer :: i
r = C_NULL_PTR ! in case of an exception return C_NULL_PTR
! use unsafe_cast_from_c_ptr to cast from c_ptr to tuple
call unsafe_cast_from_c_ptr(args, args_ptr)
! Check if the arguments are OK
ierror = args%len(num_args)
! we should also check ierror, but this example does not do complete error checking for simplicity
if (num_args /= 2) then
call raise_exception(TypeError, "update_occ_cif expects exactly 1 argument")
call args%destroy
return
endif
! Doing boring stuff
call get_atom_list_type_from_arg(args, a_p, 0)
call get_space_group_type_from_arg(args, spg, 1)
do i=1,a_p%p%natoms
pos(1:3)=a_p%p%atom(i)%x
a_p%p%atom(i)%Mult = Get_Multip_Pos(pos(1:3),SpG%p)
! This step is needed for CIF file only as the convention is different!
a_p%p%atom(i)%Occ = a_p%p%atom(i)%Occ*real(a_p%p%atom(i)%Mult)/max(1.0,real(SpG%p%Multip))
if(a_p%p%atom(i)%occ < eps) a_p%p%atom(i)%occ=real(a_p%p%atom(i)%Mult)/max(1.0,real(SpG%p%Multip))
enddo
ierror = dict_create(retval)
r = retval%get_c_ptr()
call args%destroy
end function atom_typedef_atomlist_reset_occ_cif
function atom_typedef_atomlist_set_all_adp_cif(self_ptr, args_ptr) result(r) bind(c)
type(c_ptr), value :: self_ptr
type(c_ptr), value :: args_ptr
type(c_ptr) :: r
type(tuple) :: args
type(dict) :: retval
integer :: num_args
integer :: ierror
type(Atom_List_Type_p) :: a_p
type(Crystal_Cell_type_p) :: cell
integer :: i
r = C_NULL_PTR ! in case of an exception return C_NULL_PTR
! use unsafe_cast_from_c_ptr to cast from c_ptr to tuple
call unsafe_cast_from_c_ptr(args, args_ptr)
! Check if the arguments are OK
ierror = args%len(num_args)
! we should also check ierror, but this example does not do complete error checking for simplicity
if (num_args /= 2) then
call raise_exception(TypeError, "update_occ_cif expects exactly 1 argument")