Skip to content
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
4 changes: 4 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,9 @@
# FANS Changelog

## latest

- Make PyFANS installable (also with pixi build) [#152](https://github.com/DataAnalyticsEngineering/FANS/pull/152)

## v0.7.0

- Add configurable logging using `spdlog` [#150](https://github.com/DataAnalyticsEngineering/FANS/pull/150)
Expand Down
8 changes: 2 additions & 6 deletions CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -100,12 +100,8 @@ find_package(FFTW3 REQUIRED COMPONENTS DOUBLE MPI)
option(FANS_LIBRARY_FOR_MICRO_MANAGER "Building FANS as a library to be used by the Micro Manager." OFF)

if (FANS_LIBRARY_FOR_MICRO_MANAGER)
include(FetchContent)
FetchContent_Declare(
pybind11 GIT_REPOSITORY https://github.com/pybind/pybind11.git
GIT_TAG v2.12.0
)
FetchContent_MakeAvailable(pybind11)
set(PYBIND11_FINDPYTHON ON)
find_package(pybind11 CONFIG REQUIRED)
endif()
find_package(nlohmann_json REQUIRED)
find_package(spdlog CONFIG REQUIRED)
Expand Down
3 changes: 2 additions & 1 deletion docker/Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ RUN apt-get update -qq && apt-get install -y --no-install-recommends \
# Build basics
software-properties-common \
build-essential \
# CMake + git for FetchContent + file for CPack
# CMake + git for the CI checkout + file for CPack
cmake \
git \
file \
Expand All @@ -45,6 +45,7 @@ RUN apt-get update -qq && apt-get install -y --no-install-recommends \
libspdlog-dev \
# Required for preCICE Micro Manager Python bindings
python3-dev \
pybind11-dev \
# Clean up
&& apt-get clean \
&& apt-get autoremove --purge -y \
Expand Down
3 changes: 0 additions & 3 deletions include/solver.h
Original file line number Diff line number Diff line change
Expand Up @@ -737,9 +737,6 @@ MatrixXd Solver<howmany, n_str>::get_homogenized_tangent(double pert_param)
vector<double> g0 = matmanager->get_info(0).model->macroscale_loading;
bool islinear = matmanager->all_linear;

this->reader.errorParameters["type"] = "relative";
this->TOL = max(1e-6, this->TOL);

for (auto *model : matmanager->models) {
if (dynamic_cast<J2Plasticity *>(model) != nullptr) {
throw std::runtime_error("Homogenized tangent computation not implemented for J2Plasticity models.");
Expand Down
4,341 changes: 2,322 additions & 2,019 deletions pixi.lock

Large diffs are not rendered by default.

13 changes: 9 additions & 4 deletions pixi.toml
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ fans = { path = "." }
python = ">=3.14.6,<3.15"
pytest = ">=9.1.1,<10"
pre-commit = ">=4.6.1,<5"
git = ">=2.55.0,<3"
ipykernel = ">=7.3.0,<8"
time = ">=1.10,<2"
MSUtils = ">=0.1.2,<0.2"
Expand All @@ -25,6 +26,8 @@ cmd = "cd \"$INIT_CWD\" && python -m MSUtils.general.h52xdmf {{ extra }} {{ fil

[feature.dev.dependencies]
cmake = ">=4.4.2,<5"
python = ">=3.14.6,<3.15"
pybind11 = ">=3.0.3,<4"
openmpi-mpicxx = ">=5.0.10,<6"
hdf5 = { version = ">=2.1.0,<3", build = "mpi_openmpi_*" }
fftw = { version = ">=3.3.11,<4", build = "mpi_openmpi_*" }
Expand Down Expand Up @@ -54,9 +57,10 @@ version = "0.7.0"
backend = { name = "pixi-build-cmake", version = "*" }

[package.build.config]
extra-args = [
"-DFANS_LIBRARY_FOR_MICRO_MANAGER=ON",
]
extra-args = ["-DFANS_LIBRARY_FOR_MICRO_MANAGER=ON"]

[workspace.build-variants]
python = ["3.14.*"]

[workspace.target.osx-arm64.build-variants]
cxx_compiler = ["clangxx"]
Expand All @@ -66,11 +70,12 @@ cxx_compiler = ["clangxx"]

[package.build-dependencies]
cmake = "*"
pybind11 = "*"
pkg-config = "*"
ninja = "*"

[package.host-dependencies]
python = "*"
pybind11 = "*"
hdf5 = { version = ">=1.14.6,<2", build = "mpi_openmpi_*" }
fftw = { version = "*", build = "mpi_openmpi_*" }
openmpi-mpicxx = "*"
Expand Down
21 changes: 21 additions & 0 deletions pyfans/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,27 @@ pybind11_add_module(PyFANS micro.hpp micro.cpp)
target_include_directories(PyFANS PRIVATE ${HDF5_INCLUDE_DIRS} ${FFTW3_INCLUDE_DIRS})
target_link_libraries(PyFANS PRIVATE FANS::FANS)

message(STATUS "PyFANS builds against Python ${Python_VERSION} at ${Python_EXECUTABLE}")

# Not CACHE: a cached value would survive a reconfigure against a different
# Python and install into the old version's site-packages.
if (NOT DEFINED FANS_PYTHON_SITE_DIR)
set(FANS_PYTHON_SITE_DIR
"${CMAKE_INSTALL_LIBDIR}/python${Python_VERSION_MAJOR}.${Python_VERSION_MINOR}/site-packages")
endif()

# $ORIGIN/../.. is <prefix>/lib, where libFANS.so lands.
set_target_properties(PyFANS PROPERTIES
INSTALL_RPATH "$ORIGIN/../.."
BUILD_WITH_INSTALL_RPATH FALSE)

install(
TARGETS PyFANS
LIBRARY
DESTINATION ${FANS_PYTHON_SITE_DIR}
COMPONENT FANS_Runtime
)

add_custom_command(
TARGET PyFANS
POST_BUILD
Expand Down
95 changes: 49 additions & 46 deletions pyfans/micro.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ PyFANSConfig load_config(const std::string &file_path)
}

MicroSimulation::MicroSimulation(int sim_id, bool late_init, const std::string &input_file, const std::string &config_file)
: _sim_id(sim_id)
: _sim_id(sim_id), _input_file(input_file)
{
// initialize fftw mpi
fftw_mpi_init();
Expand All @@ -48,7 +48,6 @@ MicroSimulation::MicroSimulation(int sim_id, bool late_init, const std::string &
Log::init(config.logging);

if (not late_init || sim_id >= 0) {
// Input file name is hardcoded. TODO: Make it configurable
reader.ReadInputFile(input_file);
reader.ReadMS(3);

Expand Down Expand Up @@ -87,12 +86,12 @@ py::dict MicroSimulation::solve(const py::dict &macro_data, double dt)
// Time step value dt is not used currently, but is available for future use

// Create a pybind style Numpy array from macro_write_data["micro_vector_data"], which is a Numpy array
std::vector<double> strain1 = conv_to_vector(macro_data["strains1to3"].cast<py::array_t<double>>(), 3);
std::vector<double> strain2 = conv_to_vector(macro_data["strains4to6"].cast<py::array_t<double>>(), 3);
std::vector<double> strain1 = conv_to_vector(macro_data["Strains1to3"].cast<py::array_t<double>>(), 3);
std::vector<double> strain2 = conv_to_vector(macro_data["Strains4to6"].cast<py::array_t<double>>(), 3);

std::vector<double> strain = merge_arrays(strain1, strain2);
if (not is_small_strain) {
std::vector<double> strain3 = conv_to_vector(macro_data["strains7to9"].cast<py::array_t<double>>(), 3);
std::vector<double> strain3 = conv_to_vector(macro_data["Strains7to9"].cast<py::array_t<double>>(), 3);
strain = merge_arrays(strain, strain3);
}

Expand Down Expand Up @@ -120,46 +119,46 @@ py::dict MicroSimulation::solve(const py::dict &macro_data, double dt)

// Add stress and stiffness matrix data to Python dict to be returned
if (is_small_strain) {
micro_write_data["stresses1to3"] = make_py_array(homogenized_stress[0], homogenized_stress[1], homogenized_stress[2]);
micro_write_data["stresses4to6"] = make_py_array(homogenized_stress[3], homogenized_stress[4], homogenized_stress[5]);
micro_write_data["cmat1"] = make_py_array(C(0, 0), C(0, 1), C(0, 2));
micro_write_data["cmat2"] = make_py_array(C(0, 3), C(0, 4), C(0, 5));
micro_write_data["cmat3"] = make_py_array(C(1, 1), C(1, 2), C(1, 3));
micro_write_data["cmat4"] = make_py_array(C(1, 4), C(1, 5), C(2, 2));
micro_write_data["cmat5"] = make_py_array(C(2, 3), C(2, 4), C(2, 5));
micro_write_data["cmat6"] = make_py_array(C(3, 3), C(3, 4), C(3, 5));
micro_write_data["cmat7"] = make_py_array(C(4, 4), C(4, 5), C(5, 5));
micro_write_data["Stresses1to3"] = make_py_array(homogenized_stress[0], homogenized_stress[1], homogenized_stress[2]);
micro_write_data["Stresses4to6"] = make_py_array(homogenized_stress[3], homogenized_stress[4], homogenized_stress[5]);
micro_write_data["Cmat1"] = make_py_array(C(0, 0), C(0, 1), C(0, 2));
micro_write_data["Cmat2"] = make_py_array(C(0, 3), C(0, 4), C(0, 5));
micro_write_data["Cmat3"] = make_py_array(C(1, 1), C(1, 2), C(1, 3));
micro_write_data["Cmat4"] = make_py_array(C(1, 4), C(1, 5), C(2, 2));
micro_write_data["Cmat5"] = make_py_array(C(2, 3), C(2, 4), C(2, 5));
micro_write_data["Cmat6"] = make_py_array(C(3, 3), C(3, 4), C(3, 5));
micro_write_data["Cmat7"] = make_py_array(C(4, 4), C(4, 5), C(5, 5));
} else {
micro_write_data["stresses1to3"] = make_py_array(homogenized_stress[0], homogenized_stress[1], homogenized_stress[2]);
micro_write_data["stresses4to6"] = make_py_array(homogenized_stress[3], homogenized_stress[4], homogenized_stress[5]);
micro_write_data["stresses7to9"] = make_py_array(homogenized_stress[6], homogenized_stress[7], homogenized_stress[8]);
micro_write_data["cmat1"] = make_py_array(C(0, 0), C(0, 1), C(0, 2));
micro_write_data["cmat2"] = make_py_array(C(0, 3), C(0, 4), C(0, 5));
micro_write_data["cmat3"] = make_py_array(C(0, 6), C(0, 7), C(0, 8));
micro_write_data["cmat4"] = make_py_array(C(1, 0), C(1, 1), C(1, 2));
micro_write_data["cmat5"] = make_py_array(C(1, 3), C(1, 4), C(1, 5));
micro_write_data["cmat6"] = make_py_array(C(1, 6), C(1, 7), C(1, 8));
micro_write_data["cmat7"] = make_py_array(C(2, 0), C(2, 1), C(2, 2));
micro_write_data["cmat8"] = make_py_array(C(2, 3), C(2, 4), C(2, 5));
micro_write_data["cmat9"] = make_py_array(C(2, 6), C(2, 7), C(2, 8));
micro_write_data["cmat10"] = make_py_array(C(3, 0), C(3, 1), C(3, 2));
micro_write_data["cmat11"] = make_py_array(C(3, 3), C(3, 4), C(3, 5));
micro_write_data["cmat12"] = make_py_array(C(3, 6), C(3, 7), C(3, 8));
micro_write_data["cmat13"] = make_py_array(C(4, 0), C(4, 1), C(4, 2));
micro_write_data["cmat14"] = make_py_array(C(4, 3), C(4, 4), C(4, 5));
micro_write_data["cmat15"] = make_py_array(C(4, 6), C(4, 7), C(4, 8));
micro_write_data["cmat16"] = make_py_array(C(5, 0), C(5, 1), C(5, 2));
micro_write_data["cmat17"] = make_py_array(C(5, 3), C(5, 4), C(5, 5));
micro_write_data["cmat18"] = make_py_array(C(5, 6), C(5, 7), C(5, 8));
micro_write_data["cmat19"] = make_py_array(C(6, 0), C(6, 1), C(6, 2));
micro_write_data["cmat20"] = make_py_array(C(6, 3), C(6, 4), C(6, 5));
micro_write_data["cmat21"] = make_py_array(C(6, 6), C(6, 7), C(6, 8));
micro_write_data["cmat22"] = make_py_array(C(7, 0), C(7, 1), C(7, 2));
micro_write_data["cmat23"] = make_py_array(C(7, 3), C(7, 4), C(7, 5));
micro_write_data["cmat24"] = make_py_array(C(7, 6), C(7, 7), C(7, 8));
micro_write_data["cmat25"] = make_py_array(C(8, 0), C(8, 1), C(8, 2));
micro_write_data["cmat26"] = make_py_array(C(8, 3), C(8, 4), C(8, 5));
micro_write_data["cmat27"] = make_py_array(C(8, 6), C(8, 7), C(8, 8));
micro_write_data["Stresses1to3"] = make_py_array(homogenized_stress[0], homogenized_stress[1], homogenized_stress[2]);
micro_write_data["Stresses4to6"] = make_py_array(homogenized_stress[3], homogenized_stress[4], homogenized_stress[5]);
micro_write_data["Stresses7to9"] = make_py_array(homogenized_stress[6], homogenized_stress[7], homogenized_stress[8]);
micro_write_data["Cmat1"] = make_py_array(C(0, 0), C(0, 1), C(0, 2));
micro_write_data["Cmat2"] = make_py_array(C(0, 3), C(0, 4), C(0, 5));
micro_write_data["Cmat3"] = make_py_array(C(0, 6), C(0, 7), C(0, 8));
micro_write_data["Cmat4"] = make_py_array(C(1, 0), C(1, 1), C(1, 2));
micro_write_data["Cmat5"] = make_py_array(C(1, 3), C(1, 4), C(1, 5));
micro_write_data["Cmat6"] = make_py_array(C(1, 6), C(1, 7), C(1, 8));
micro_write_data["Cmat7"] = make_py_array(C(2, 0), C(2, 1), C(2, 2));
micro_write_data["Cmat8"] = make_py_array(C(2, 3), C(2, 4), C(2, 5));
micro_write_data["Cmat9"] = make_py_array(C(2, 6), C(2, 7), C(2, 8));
micro_write_data["Cmat10"] = make_py_array(C(3, 0), C(3, 1), C(3, 2));
micro_write_data["Cmat11"] = make_py_array(C(3, 3), C(3, 4), C(3, 5));
micro_write_data["Cmat12"] = make_py_array(C(3, 6), C(3, 7), C(3, 8));
micro_write_data["Cmat13"] = make_py_array(C(4, 0), C(4, 1), C(4, 2));
micro_write_data["Cmat14"] = make_py_array(C(4, 3), C(4, 4), C(4, 5));
micro_write_data["Cmat15"] = make_py_array(C(4, 6), C(4, 7), C(4, 8));
micro_write_data["Cmat16"] = make_py_array(C(5, 0), C(5, 1), C(5, 2));
micro_write_data["Cmat17"] = make_py_array(C(5, 3), C(5, 4), C(5, 5));
micro_write_data["Cmat18"] = make_py_array(C(5, 6), C(5, 7), C(5, 8));
micro_write_data["Cmat19"] = make_py_array(C(6, 0), C(6, 1), C(6, 2));
micro_write_data["Cmat20"] = make_py_array(C(6, 3), C(6, 4), C(6, 5));
micro_write_data["Cmat21"] = make_py_array(C(6, 6), C(6, 7), C(6, 8));
micro_write_data["Cmat22"] = make_py_array(C(7, 0), C(7, 1), C(7, 2));
micro_write_data["Cmat23"] = make_py_array(C(7, 3), C(7, 4), C(7, 5));
micro_write_data["Cmat24"] = make_py_array(C(7, 6), C(7, 7), C(7, 8));
micro_write_data["Cmat25"] = make_py_array(C(8, 0), C(8, 1), C(8, 2));
micro_write_data["Cmat26"] = make_py_array(C(8, 3), C(8, 4), C(8, 5));
micro_write_data["Cmat27"] = make_py_array(C(8, 6), C(8, 7), C(8, 8));
}

return micro_write_data;
Expand All @@ -176,7 +175,7 @@ void MicroSimulation::set_state(const py::dict &state)
{
// TODO read from state, not file
reader.FreeMS();
reader.ReadInputFile("input.json");
reader.ReadInputFile(_input_file);
reader.ReadMS(3);
if (reader.strain_type == "small") {
delete std::get<MaterialManager<3, 6> *>(matmanager);
Expand Down Expand Up @@ -212,7 +211,11 @@ PYBIND11_MODULE(PyFANS, m)
m.doc() = "FANS for Micro Manager";

py::class_<MicroSimulation>(m, "MicroSimulation")
.def(py::init<int>())
.def(py::init<int, bool, const std::string &, const std::string &>(),
py::arg("sim_id"),
py::arg("late_init") = false,
py::arg("input_file") = "input.json",
py::arg("config_file") = "pyfans-config.json")
.def("solve", &MicroSimulation::solve, py::return_value_policy::automatic)
.def("set_state", &MicroSimulation::set_state)
.def("get_state", &MicroSimulation::get_state, py::return_value_policy::automatic)
Expand Down
5 changes: 3 additions & 2 deletions pyfans/micro.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -30,8 +30,9 @@ class MicroSimulation {
void set_id(int id);

private:
int _sim_id;
Reader reader;
int _sim_id;
std::string _input_file;
Reader reader;
// Hardcoding mechanical models because these definitions need information from the input file.
using matmanager_t = std::variant<MaterialManager<3, 6> *, MaterialManager<3, 9> *>;
using solver_t = std::variant<Solver<3, 6> *, Solver<3, 9> *>;
Expand Down
24 changes: 12 additions & 12 deletions test/test_pyfans/macro-cube.py
Original file line number Diff line number Diff line change
Expand Up @@ -42,18 +42,18 @@ def main():

# time loop
while participant.is_coupling_ongoing():
stress1to3 = participant.read_data(mesh_name, "stresses1to3", vertex_ids, dt)
stress4to6 = participant.read_data(mesh_name, "stresses4to6", vertex_ids, dt)
cmat1 = participant.read_data(mesh_name, "cmat1", vertex_ids, dt)
cmat2 = participant.read_data(mesh_name, "cmat2", vertex_ids, dt)
cmat3 = participant.read_data(mesh_name, "cmat3", vertex_ids, dt)
cmat4 = participant.read_data(mesh_name, "cmat4", vertex_ids, dt)
cmat5 = participant.read_data(mesh_name, "cmat5", vertex_ids, dt)
cmat6 = participant.read_data(mesh_name, "cmat6", vertex_ids, dt)
cmat7 = participant.read_data(mesh_name, "cmat7", vertex_ids, dt)

participant.write_data(mesh_name, "strains1to3", vertex_ids, strains1to3)
participant.write_data(mesh_name, "strains4to6", vertex_ids, strains4to6)
stress1to3 = participant.read_data(mesh_name, "Stresses1to3", vertex_ids, dt)
stress4to6 = participant.read_data(mesh_name, "Stresses4to6", vertex_ids, dt)
cmat1 = participant.read_data(mesh_name, "Cmat1", vertex_ids, dt)
cmat2 = participant.read_data(mesh_name, "Cmat2", vertex_ids, dt)
cmat3 = participant.read_data(mesh_name, "Cmat3", vertex_ids, dt)
cmat4 = participant.read_data(mesh_name, "Cmat4", vertex_ids, dt)
cmat5 = participant.read_data(mesh_name, "Cmat5", vertex_ids, dt)
cmat6 = participant.read_data(mesh_name, "Cmat6", vertex_ids, dt)
cmat7 = participant.read_data(mesh_name, "Cmat7", vertex_ids, dt)

participant.write_data(mesh_name, "Strains1to3", vertex_ids, strains1to3)
participant.write_data(mesh_name, "Strains4to6", vertex_ids, strains4to6)

participant.advance(dt)
dt = participant.get_max_time_step_size()
Expand Down
4 changes: 2 additions & 2 deletions test/test_pyfans/micro-manager-config.json
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,8 @@
"coupling_params": {
"precice_config_file_name": "precice-config.xml",
"macro_mesh_name": "cube",
"read_data_names": ["strains1to3", "strains4to6"],
"write_data_names": ["stresses1to3", "stresses4to6", "cmat1", "cmat2", "cmat3", "cmat4", "cmat5", "cmat6", "cmat7"]
"read_data_names": ["Strains1to3", "Strains4to6"],
"write_data_names": ["Stresses1to3", "Stresses4to6", "Cmat1", "Cmat2", "Cmat3", "Cmat4", "Cmat5", "Cmat6", "Cmat7"]
},
"simulation_params": {
"micro_dt": 1e-1,
Expand Down
Loading