From 893bf0917d3456cc21af0970b5059ede13b0ae3e Mon Sep 17 00:00:00 2001 From: Alex Hocks Date: Wed, 6 May 2026 12:04:56 +0200 Subject: [PATCH 1/6] enable switching of MPI comm --- include/reader.h | 5 + include/solver.h | 28 +++--- include/solverCG.h | 2 +- src/reader.cpp | 223 ++++++++++++++++++++++++--------------------- 4 files changed, 138 insertions(+), 120 deletions(-) diff --git a/include/reader.h b/include/reader.h index cccff29..eab42d1 100644 --- a/include/reader.h +++ b/include/reader.h @@ -21,6 +21,10 @@ class Reader { // Destructor to free allocated memory ~Reader(); + // MPI controlls + bool force_single_rank; + MPI_Comm communicator; + // contents of input file: char ms_filename[4096]{}; // Name of Micro-structure hdf5 file char ms_datasetname[4096]{}; // Absolute path of Micro-structure in hdf5 file @@ -64,6 +68,7 @@ class Reader { // void Setup(ptrdiff_t howmany); void ReadInputFile(char input_fn[]); + void ReadJson(const json &j); void ReadMS(int hm); void ComputeVolumeFractions(); // void ReadHDF5(char file_name[], char dset_name[]); diff --git a/include/solver.h b/include/solver.h index a058224..3858a42 100644 --- a/include/solver.h +++ b/include/solver.h @@ -18,6 +18,7 @@ class Solver : private MixedBCController { const int world_rank; const int world_size; + MPI_Comm communicator; const ptrdiff_t n_x, n_y, n_z; // NOTE: the order in the declaration is very important because it is the same order in which the later initialization via member initializer lists takes place @@ -109,6 +110,7 @@ Solver::Solver(Reader &reader, MaterialManager * matmanager(matmgr), world_rank(reader.world_rank), world_size(reader.world_size), + communicator(reader.communicator), n_x(reader.dims[0]), n_y(reader.dims[1]), n_z(reader.dims[2]), @@ -219,8 +221,8 @@ void Solver::CreateFFTWPlans(double *in, fftw_complex *transform // But, according to https://fftw.org/doc/MPI-Plan-Creation.html the BLOCK sizes must be the same: // "These must be the same block sizes as were passed to the corresponding ‘local_size’ function" const ptrdiff_t n[3] = {n_x, n_y, n_z}; - planfft = fftw_mpi_plan_many_dft_r2c(rank, n, howmany, iblock, oblock, in, transformed, MPI_COMM_WORLD, FFTW_MEASURE | FFTW_MPI_TRANSPOSED_OUT); - planifft = fftw_mpi_plan_many_dft_c2r(rank, n, howmany, iblock, oblock, transformed, out, MPI_COMM_WORLD, FFTW_MEASURE | FFTW_MPI_TRANSPOSED_IN); + planfft = fftw_mpi_plan_many_dft_r2c(rank, n, howmany, iblock, oblock, in, transformed, communicator, FFTW_MEASURE | FFTW_MPI_TRANSPOSED_OUT); + planifft = fftw_mpi_plan_many_dft_c2r(rank, n, howmany, iblock, oblock, transformed, out, communicator, FFTW_MEASURE | FFTW_MPI_TRANSPOSED_IN); // see https://eigen.tuxfamily.org/dox/group__TutorialMapClass.html#title3 new (&rhat) Map((std::complex *) transformed, local_n1 * n_x * (n_z / 2 + 1) * howmany); @@ -243,7 +245,7 @@ void Solver::compute_residual_basic(RealArray &r_matrix, RealArr // int MPI_Sendrecv(void *sendbuf, int sendcount, MPI_Datatype sendtype, int dest, int sendtag, void *recvbuf, // int recvcount, MPI_Datatype recvtype, int source, int recvtag, MPI_Comm comm, MPI_Status *status) MPI_Sendrecv(u, n_y * n_z * howmany, MPI_DOUBLE, (world_rank + world_size - 1) % world_size, 0, - u + local_n0 * n_y * n_z * howmany, n_y * n_z * howmany, MPI_DOUBLE, (world_rank + 1) % world_size, 0, MPI_COMM_WORLD, MPI_STATUS_IGNORE); + u + local_n0 * n_y * n_z * howmany, n_y * n_z * howmany, MPI_DOUBLE, (world_rank + 1) % world_size, 0, communicator, MPI_STATUS_IGNORE); Matrix ue; @@ -263,7 +265,7 @@ void Solver::compute_residual_basic(RealArray &r_matrix, RealArr }); MPI_Sendrecv(r + local_n0 * n_y * (n_z + padding) * howmany, n_y * (n_z + padding) * howmany, MPI_DOUBLE, (world_rank + 1) % world_size, 0, - buffer_padding, n_y * (n_z + padding) * howmany, MPI_DOUBLE, (world_rank + world_size - 1) % world_size, 0, MPI_COMM_WORLD, MPI_STATUS_IGNORE); + buffer_padding, n_y * (n_z + padding) * howmany, MPI_DOUBLE, (world_rank + world_size - 1) % world_size, 0, communicator, MPI_STATUS_IGNORE); RealArray b(buffer_padding, n_z * howmany, n_y, OuterStride<>((n_z + padding) * howmany)); // NOTE: for any padding of more than 2, the buffer_padding has to be extended @@ -428,7 +430,7 @@ double Solver::compute_error(RealArray &r) } double err; - MPI_Allreduce(&err_local, &err, 1, MPI_DOUBLE, MPI_MAX, MPI_COMM_WORLD); + MPI_Allreduce(&err_local, &err, 1, MPI_DOUBLE, MPI_MAX, communicator); err_all[iter] = err; double err0 = err_all[0]; @@ -496,7 +498,7 @@ void Solver::postprocess(Reader &reader, int load_idx, int time_ vector phase_counts(n_mat, 0); MPI_Sendrecv(v_u, n_y * n_z * howmany, MPI_DOUBLE, (world_rank + world_size - 1) % world_size, 0, - v_u + local_n0 * n_y * n_z * howmany, n_y * n_z * howmany, MPI_DOUBLE, (world_rank + 1) % world_size, 0, MPI_COMM_WORLD, MPI_STATUS_IGNORE); + v_u + local_n0 * n_y * n_z * howmany, n_y * n_z * howmany, MPI_DOUBLE, (world_rank + 1) % world_size, 0, communicator, MPI_STATUS_IGNORE); Matrix ue; int phase_id; @@ -556,16 +558,16 @@ void Solver::postprocess(Reader &reader, int load_idx, int time_ }); } - MPI_Allreduce(MPI_IN_PLACE, stress_average.data(), n_str, MPI_DOUBLE, MPI_SUM, MPI_COMM_WORLD); - MPI_Allreduce(MPI_IN_PLACE, strain_average.data(), n_str, MPI_DOUBLE, MPI_SUM, MPI_COMM_WORLD); + MPI_Allreduce(MPI_IN_PLACE, stress_average.data(), n_str, MPI_DOUBLE, MPI_SUM, communicator); + MPI_Allreduce(MPI_IN_PLACE, strain_average.data(), n_str, MPI_DOUBLE, MPI_SUM, communicator); stress_average /= (n_x * n_y * n_z); strain_average /= (n_x * n_y * n_z); // Reduce per-phase accumulations across all processes for (int mat_index = 0; mat_index < n_mat; ++mat_index) { - MPI_Allreduce(MPI_IN_PLACE, phase_stress_average[mat_index].data(), n_str, MPI_DOUBLE, MPI_SUM, MPI_COMM_WORLD); - MPI_Allreduce(MPI_IN_PLACE, phase_strain_average[mat_index].data(), n_str, MPI_DOUBLE, MPI_SUM, MPI_COMM_WORLD); - MPI_Allreduce(MPI_IN_PLACE, &phase_counts[mat_index], 1, MPI_INT, MPI_SUM, MPI_COMM_WORLD); + MPI_Allreduce(MPI_IN_PLACE, phase_stress_average[mat_index].data(), n_str, MPI_DOUBLE, MPI_SUM, communicator); + MPI_Allreduce(MPI_IN_PLACE, phase_strain_average[mat_index].data(), n_str, MPI_DOUBLE, MPI_SUM, communicator); + MPI_Allreduce(MPI_IN_PLACE, &phase_counts[mat_index], 1, MPI_INT, MPI_SUM, communicator); // Compute average for each phase if (phase_counts[mat_index] > 0) { @@ -716,7 +718,7 @@ VectorXd Solver::get_homogenized_stress() homogenized_stress = VectorXd::Zero(n_str); MPI_Sendrecv(v_u, n_y * n_z * howmany, MPI_DOUBLE, (world_rank + world_size - 1) % world_size, 0, - v_u + local_n0 * n_y * n_z * howmany, n_y * n_z * howmany, MPI_DOUBLE, (world_rank + 1) % world_size, 0, MPI_COMM_WORLD, MPI_STATUS_IGNORE); + v_u + local_n0 * n_y * n_z * howmany, n_y * n_z * howmany, MPI_DOUBLE, (world_rank + 1) % world_size, 0, communicator, MPI_STATUS_IGNORE); Matrix ue; int phase_id; @@ -733,7 +735,7 @@ VectorXd Solver::get_homogenized_stress() homogenized_stress += stress.segment(n_str * idx[0], n_str); }); - MPI_Allreduce(MPI_IN_PLACE, homogenized_stress.data(), n_str, MPI_DOUBLE, MPI_SUM, MPI_COMM_WORLD); + MPI_Allreduce(MPI_IN_PLACE, homogenized_stress.data(), n_str, MPI_DOUBLE, MPI_SUM, communicator); homogenized_stress /= (n_x * n_y * n_z); return homogenized_stress; diff --git a/include/solverCG.h b/include/solverCG.h index 842317c..2d07849 100644 --- a/include/solverCG.h +++ b/include/solverCG.h @@ -55,7 +55,7 @@ double SolverCG::dotProduct(RealArray &a, RealArray &b) { double local_value = (a * b).sum(); double result; - MPI_Allreduce(&local_value, &result, 1, MPI_DOUBLE, MPI_SUM, MPI_COMM_WORLD); + MPI_Allreduce(&local_value, &result, 1, MPI_DOUBLE, MPI_SUM, this->communicator); return result; } diff --git a/src/reader.cpp b/src/reader.cpp index 13251cd..72e7d7a 100644 --- a/src/reader.cpp +++ b/src/reader.cpp @@ -29,8 +29,8 @@ void Reader::ComputeVolumeFractions() // Find the global maximum and minimum material indices unsigned short global_max, global_min; - MPI_Allreduce(&local_max, &global_max, 1, MPI_UNSIGNED_SHORT, MPI_MAX, MPI_COMM_WORLD); - MPI_Allreduce(&local_min, &global_min, 1, MPI_UNSIGNED_SHORT, MPI_MIN, MPI_COMM_WORLD); + MPI_Allreduce(&local_max, &global_max, 1, MPI_UNSIGNED_SHORT, MPI_MAX, communicator); + MPI_Allreduce(&local_min, &global_min, 1, MPI_UNSIGNED_SHORT, MPI_MIN, communicator); // Calculate total number of materials n_mat = global_max - global_min + 1; @@ -52,7 +52,7 @@ void Reader::ComputeVolumeFractions() for (int i = 0; i < n_mat; i++) { long vf; - MPI_Allreduce(&(vol_frac[i]), &vf, 1, MPI_LONG, MPI_SUM, MPI_COMM_WORLD); + MPI_Allreduce(&(vol_frac[i]), &vf, 1, MPI_LONG, MPI_SUM, communicator); v_frac[i] = double(vf) / double(dims[0] * dims[1] * dims[2]); if (world_rank == 0) printf("# material %4u vol. frac. %10.4f%% \n", @@ -63,122 +63,133 @@ void Reader::ComputeVolumeFractions() void Reader ::ReadInputFile(char input_fn[]) { try { - - MPI_Comm_rank(MPI_COMM_WORLD, &world_rank); - MPI_Comm_size(MPI_COMM_WORLD, &world_size); - ifstream i(input_fn); json j; i >> j; - inputJson = j; // Store complete input JSON for MaterialManager - - microstructure = j["microstructure"]; - std::snprintf(ms_filename, sizeof(ms_filename), "%s", microstructure["filepath"].get().c_str()); - // dataset name handling - const auto tmp_str = microstructure["datasetname"].get(); - if (tmp_str.empty()) - throw std::invalid_argument("datasetname must not be empty and must refer to a valid HDF5 path"); - // Ensure absolute HDF5 path, leading slash - std::snprintf(ms_datasetname, sizeof(ms_datasetname), "%s%s", tmp_str.front() == '/' ? "" : "/", tmp_str.c_str()); - L = microstructure["L"].get>(); - - if (j.contains("results_prefix")) { - std::snprintf(results_prefix, sizeof(results_prefix), "%s", j["results_prefix"].get().c_str()); - } else { - strcpy(results_prefix, ""); - } - - // Construct dataset_name as "_results/" - std::snprintf(dataset_name, sizeof(dataset_name), "%s_results/%s", ms_datasetname, results_prefix); + ReadJson(j); + } catch (const std::exception &e) { + fprintf(stderr, "ERROR trying to read input file '%s' for FANS: %s\n", input_fn, e.what()); + exit(10); + } +} - errorParameters = j["error_parameters"]; - TOL = errorParameters["tolerance"].get(); - n_it = j["n_it"].get(); +void Reader::ReadJson(const json &j) +{ + inputJson = j; // Store complete input JSON for MaterialManager - extrapolate_displacement = j.value("extrapolate_displacement", extrapolate_displacement); + if (j.contains("no_mpi") && j["no_mpi"].get()) { + force_single_rank = true; + communicator = MPI_COMM_SELF; + } else { + force_single_rank = false; + communicator = MPI_COMM_WORLD; + } - if (j.contains("linesearch_parameters")) { - ls_max_iter = j["linesearch_parameters"].value("max_iter", ls_max_iter); - ls_tol = j["linesearch_parameters"].value("tol", ls_tol); - if (ls_max_iter < 1 || ls_tol <= 0.0) - throw std::invalid_argument("linesearch_parameters: max_iter >= 1 and tol > 0 required"); - } + MPI_Comm_rank(communicator, &world_rank); + MPI_Comm_size(communicator, &world_size); + + microstructure = j["microstructure"]; + std::snprintf(ms_filename, sizeof(ms_filename), "%s", microstructure["filepath"].get().c_str()); + // dataset name handling + const auto tmp_str = microstructure["datasetname"].get(); + if (tmp_str.empty()) + throw std::invalid_argument("datasetname must not be empty and must refer to a valid HDF5 path"); + // Ensure absolute HDF5 path, leading slash + std::snprintf(ms_datasetname, sizeof(ms_datasetname), "%s%s", tmp_str.front() == '/' ? "" : "/", tmp_str.c_str()); + L = microstructure["L"].get>(); + + if (j.contains("results_prefix")) { + std::snprintf(results_prefix, sizeof(results_prefix), "%s", j["results_prefix"].get().c_str()); + } else { + strcpy(results_prefix, ""); + } - problemType = j["problem_type"].get(); - method = j["method"].get(); + // Construct dataset_name as "_results/" + std::snprintf(dataset_name, sizeof(dataset_name), "%s_results/%s", ms_datasetname, results_prefix); - // Parse strain_type (optional, defaults to "small") - if (j.contains("strain_type")) { - strain_type = j["strain_type"].get(); - if (strain_type != "small" && strain_type != "large") { - throw std::invalid_argument("strain_type must be either 'small' or 'large'"); - } - } else { - strain_type = "small"; // Default to small strain - } + errorParameters = j["error_parameters"]; + TOL = errorParameters["tolerance"].get(); + n_it = j["n_it"].get(); - // Parse FE_type (optional, defaults to "HEX8") - if (j.contains("FE_type")) { - FE_type = j["FE_type"].get(); - if (FE_type != "HEX8" && FE_type != "HEX8R" && FE_type != "BBAR") { - throw std::invalid_argument("FE_type must be one of: 'HEX8', 'HEX8R', or 'BBAR'"); - } - } else { - FE_type = "HEX8"; // Default to full integration - } + extrapolate_displacement = j.value("extrapolate_displacement", extrapolate_displacement); - resultsToWrite = j["results"].get>(); // Read the results_to_write field + if (j.contains("linesearch_parameters")) { + ls_max_iter = j["linesearch_parameters"].value("max_iter", ls_max_iter); + ls_tol = j["linesearch_parameters"].value("tol", ls_tol); + if (ls_max_iter < 1 || ls_tol <= 0.0) + throw std::invalid_argument("linesearch_parameters: max_iter >= 1 and tol > 0 required"); + } - load_cases.clear(); - const auto &ml = j["macroscale_loading"]; - if (!ml.is_array()) - throw std::runtime_error("macroscale_loading must be an array"); + problemType = j["problem_type"].get(); + method = j["method"].get(); - // Determine the size of loading vector based on problem type and strain formulation - int n_str; - if (problemType == "thermal") { - n_str = 3; // Temperature gradient components - } else if (strain_type == "large") { - n_str = 9; // Deformation gradient components (F11, F12, F13, F21, F22, F23, F31, F32, F33) - } else { - n_str = 6; // Small strain components (eps11, eps22, eps33, eps12, eps13, eps23) + // Parse strain_type (optional, defaults to "small") + if (j.contains("strain_type")) { + strain_type = j["strain_type"].get(); + if (strain_type != "small" && strain_type != "large") { + throw std::invalid_argument("strain_type must be either 'small' or 'large'"); } + } else { + strain_type = "small"; // Default to small strain + } - for (const auto &entry : ml) { - LoadCase lc; - if (entry.is_array()) { // ---------- legacy pure-strain ---------- - lc.mixed = false; - lc.g0_path = entry.get>>(); - lc.n_steps = lc.g0_path.size(); - if (lc.g0_path[0].size() != static_cast(n_str)) - throw std::invalid_argument("Invalid length of loading vector: expected " + - std::to_string(n_str) + " components but got " + - std::to_string(lc.g0_path[0].size())); - } else { // ---------- mixed BC object ------------ - lc.mixed = true; - lc.mbc = MixedBC::from_json(entry, n_str); - lc.n_steps = lc.mbc.F_E_path.rows(); - } - load_cases.push_back(std::move(lc)); + // Parse FE_type (optional, defaults to "HEX8") + if (j.contains("FE_type")) { + FE_type = j["FE_type"].get(); + if (FE_type != "HEX8" && FE_type != "HEX8R" && FE_type != "BBAR") { + throw std::invalid_argument("FE_type must be one of: 'HEX8', 'HEX8R', or 'BBAR'"); } + } else { + FE_type = "HEX8"; // Default to full integration + } + + resultsToWrite = j["results"].get>(); // Read the results_to_write field - if (world_rank == 0) { - printf("# microstructure file name: \t '%s'\n", ms_filename); - printf("# microstructure dataset name: \t '%s'\n", ms_datasetname); - printf("# strain type: \t %s\n", strain_type.c_str()); - printf("# problem type: \t %s\n", problemType.c_str()); - printf("# FE type: \t %s\n", FE_type.c_str()); - printf( - "# FANS error measure: \t %s %s error \n", - errorParameters["type"].get().c_str(), - errorParameters["measure"].get().c_str()); - printf("# FANS Tolerance: \t %10.5e\n", errorParameters["tolerance"].get()); - printf("# Max iterations: \t %6i\n", n_it); + load_cases.clear(); + const auto &ml = j["macroscale_loading"]; + if (!ml.is_array()) + throw std::runtime_error("macroscale_loading must be an array"); + + // Determine the size of loading vector based on problem type and strain formulation + int n_str; + if (problemType == "thermal") { + n_str = 3; // Temperature gradient components + } else if (strain_type == "large") { + n_str = 9; // Deformation gradient components (F11, F12, F13, F21, F22, F23, F31, F32, F33) + } else { + n_str = 6; // Small strain components (eps11, eps22, eps33, eps12, eps13, eps23) + } + + for (const auto &entry : ml) { + LoadCase lc; + if (entry.is_array()) { // ---------- legacy pure-strain ---------- + lc.mixed = false; + lc.g0_path = entry.get>>(); + lc.n_steps = lc.g0_path.size(); + if (lc.g0_path[0].size() != static_cast(n_str)) + throw std::invalid_argument("Invalid length of loading vector: expected " + + std::to_string(n_str) + " components but got " + + std::to_string(lc.g0_path[0].size())); + } else { // ---------- mixed BC object ------------ + lc.mixed = true; + lc.mbc = MixedBC::from_json(entry, n_str); + lc.n_steps = lc.mbc.F_E_path.rows(); } + load_cases.push_back(std::move(lc)); + } - } catch (const std::exception &e) { - fprintf(stderr, "ERROR trying to read input file '%s' for FANS: %s\n", input_fn, e.what()); - exit(10); + if (world_rank == 0) { + printf("# microstructure file name: \t '%s'\n", ms_filename); + printf("# microstructure dataset name: \t '%s'\n", ms_datasetname); + printf("# strain type: \t %s\n", strain_type.c_str()); + printf("# problem type: \t %s\n", problemType.c_str()); + printf("# FE type: \t %s\n", FE_type.c_str()); + printf( + "# FANS error measure: \t %s %s error \n", + errorParameters["type"].get().c_str(), + errorParameters["measure"].get().c_str()); + printf("# FANS Tolerance: \t %10.5e\n", errorParameters["tolerance"].get()); + printf("# Max iterations: \t %6i\n", n_it); } } @@ -236,8 +247,8 @@ void Reader ::ReadMS(int hm) hid_t plist_id; /* property list identifier */ herr_t status; - MPI_Comm_rank(MPI_COMM_WORLD, &world_rank); - MPI_Comm_size(MPI_COMM_WORLD, &world_size); + MPI_Comm_rank(communicator, &world_rank); + MPI_Comm_size(communicator, &world_size); MPI_Info info = MPI_INFO_NULL; // Set up file access property list with parallel I/O access @@ -324,11 +335,11 @@ void Reader ::ReadMS(int hm) ptrdiff_t *local_n1, ptrdiff_t *local_1_start); \ */ - alloc_local = fftw_mpi_local_size_many_transposed(rank, n, hm, block0, block1, MPI_COMM_WORLD, &local_n0, &local_0_start, &local_n1, &local_1_start); + alloc_local = fftw_mpi_local_size_many_transposed(rank, n, hm, block0, block1, communicator, &local_n0, &local_0_start, &local_n1, &local_1_start); if (local_n0 < 4) throw std::runtime_error("[ FANS3D_Grid ] ERROR: Number of voxels in x-direction is less than 4 in process " + to_string(world_rank)); - MPI_Barrier(MPI_COMM_WORLD); + MPI_Barrier(communicator); hsize_t fcount[3], foffset[3]; if (is_zyx) { /* file layout Z Y X */ @@ -414,7 +425,7 @@ void Reader::OpenResultsFile(const char *output_fn) { std::snprintf(results_filename, sizeof(results_filename), "%s", output_fn); hid_t plist_id = H5Pcreate(H5P_FILE_ACCESS); - H5Pset_fapl_mpio(plist_id, MPI_COMM_WORLD, MPI_INFO_NULL); + H5Pset_fapl_mpio(plist_id, communicator, MPI_INFO_NULL); results_file_id = H5Fcreate(results_filename, H5F_ACC_TRUNC, H5P_DEFAULT, plist_id); H5Pclose(plist_id); From 461b84693eabda23ab18e526af88698c39b94a2d Mon Sep 17 00:00:00 2001 From: Alex Hocks Date: Wed, 6 May 2026 12:15:33 +0200 Subject: [PATCH 2/6] add changelog --- CHANGELOG.md | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index d2e2f7e..ef87b7c 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,9 @@ # FANS Changelog +## latest + +- Added switching of MPI communicator [#139](https://github.com/DataAnalyticsEngineering/FANS/pull/139) + ## v0.6.2 - Fix bug demanding a leading slash in the datasetname and exiting ungracefully [#123](https://github.com/DataAnalyticsEngineering/FANS/pull/123) From a9548e2c9ccdca9c52c73347388c89b2e3b3d17a Mon Sep 17 00:00:00 2001 From: Alex Hocks Date: Wed, 6 May 2026 12:23:57 +0200 Subject: [PATCH 3/6] add doc --- README.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/README.md b/README.md index 90b6a6a..0a63512 100644 --- a/README.md +++ b/README.md @@ -292,6 +292,7 @@ FANS requires a JSON input file specifying the problem parameters. Example input "tolerance": 1e-10 }, "n_it": 100, +"no_mpi": true, ``` - `FE_type`: This specifies the type of finite element to be used. Common options include: @@ -304,6 +305,7 @@ FANS requires a JSON input file specifying the problem parameters. Example input - `type`: Defines the type of error measurement. Options are `absolute` or `relative`. - `tolerance`: Sets the tolerance level for the solver, defining the convergence criterion based on the chosen error measure. The solver iterates until the solution meets this tolerance. - `n_it`: Specifies the maximum number of iterations allowed for the FANS solver. +- `no_mpi`: Turns off parallel MPI computation and forces the usage of `MPI_COMM_SELF` ### Macroscale loading conditions From 961798bc43e92c47bd2616915666cc7a3a1886be Mon Sep 17 00:00:00 2001 From: Alex Hocks Date: Wed, 6 May 2026 12:27:36 +0200 Subject: [PATCH 4/6] fix pixi lock file --- pixi.lock | 71 ++++++++++++++++++++++++++++++++++++++++++++++++------- 1 file changed, 63 insertions(+), 8 deletions(-) diff --git a/pixi.lock b/pixi.lock index e035181..3ce0791 100644 --- a/pixi.lock +++ b/pixi.lock @@ -1592,7 +1592,7 @@ environments: - conda: https://conda.anaconda.org/conda-forge/linux-64/eigen-abi-5.0.1.80-hf414acd_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/exceptiongroup-1.3.1-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/executing-2.2.1-pyhd8ed1ab_0.conda - - conda: https://conda.anaconda.org/conda-forge/linux-64/fftw-3.3.10-mpi_openmpi_h76e6d66_12.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/fftw-3.3.11-mpi_openmpi_h76e6d66_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/filelock-3.29.0-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/fmt-12.1.0-hff5e90c_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/font-ttf-dejavu-sans-mono-2.37-hab24e00_0.tar.bz2 @@ -2000,7 +2000,7 @@ environments: - conda: https://conda.anaconda.org/conda-forge/linux-aarch64/eigen-abi-5.0.1.100-h9a8c16c_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/exceptiongroup-1.3.1-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/executing-2.2.1-pyhd8ed1ab_0.conda - - conda: https://conda.anaconda.org/conda-forge/linux-aarch64/fftw-3.3.10-mpi_openmpi_h79548e3_12.conda + - conda: https://conda.anaconda.org/conda-forge/linux-aarch64/fftw-3.3.11-mpi_openmpi_h79548e3_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/filelock-3.29.0-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/linux-aarch64/fmt-12.1.0-h20c602a_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/font-ttf-dejavu-sans-mono-2.37-hab24e00_0.tar.bz2 @@ -2407,7 +2407,7 @@ environments: - conda: https://conda.anaconda.org/conda-forge/osx-64/eigen-abi-5.0.1.80-h969a130_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/exceptiongroup-1.3.1-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/executing-2.2.1-pyhd8ed1ab_0.conda - - conda: https://conda.anaconda.org/conda-forge/osx-64/fftw-3.3.10-mpi_openmpi_h63b4274_12.conda + - conda: https://conda.anaconda.org/conda-forge/osx-64/fftw-3.3.11-mpi_openmpi_h63b4274_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/filelock-3.29.0-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-64/fmt-12.1.0-hda137b5_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/font-ttf-dejavu-sans-mono-2.37-hab24e00_0.tar.bz2 @@ -2768,7 +2768,7 @@ environments: - conda: https://conda.anaconda.org/conda-forge/osx-arm64/eigen-abi-5.0.1.100-h485a483_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/exceptiongroup-1.3.1-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/executing-2.2.1-pyhd8ed1ab_0.conda - - conda: https://conda.anaconda.org/conda-forge/osx-arm64/fftw-3.3.10-mpi_openmpi_h83537c7_12.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/fftw-3.3.11-mpi_openmpi_h83537c7_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/filelock-3.29.0-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/fmt-12.1.0-h403dcb5_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/font-ttf-dejavu-sans-mono-2.37-hab24e00_0.tar.bz2 @@ -6402,7 +6402,7 @@ packages: depends: - libcxx >=22 - hdf5 >=1.14.6,<1.14.7.0a0 mpi_openmpi_* - - fftw >=3.3.10,<4.0a0 + - fftw >=3.3.11,<4.0a0 - fftw * mpi_openmpi_* - conda: . name: fans @@ -6416,7 +6416,7 @@ packages: - libstdcxx >=15 - libgcc >=15 - hdf5 >=1.14.6,<1.14.7.0a0 mpi_openmpi_* - - fftw >=3.3.10,<4.0a0 + - fftw >=3.3.11,<4.0a0 - fftw * mpi_openmpi_* - conda: . name: fans @@ -6429,7 +6429,7 @@ packages: depends: - libcxx >=22 - hdf5 >=1.14.6,<1.14.7.0a0 mpi_openmpi_* - - fftw >=3.3.10,<4.0a0 + - fftw >=3.3.11,<4.0a0 - fftw * mpi_openmpi_* - conda: . name: fans @@ -6442,7 +6442,7 @@ packages: - libstdcxx >=15 - libgcc >=15 - hdf5 >=1.14.6,<1.14.7.0a0 mpi_openmpi_* - - fftw >=3.3.10,<4.0a0 + - fftw >=3.3.11,<4.0a0 - fftw * mpi_openmpi_* - conda: https://conda.anaconda.org/conda-forge/linux-64/fftw-3.3.10-mpi_openmpi_h76e6d66_12.conda sha256: 66b0fe5b5e52565ee7120f5e897b1d206494c0440b7c00d4ec0ae09549e1cedc @@ -6471,6 +6471,20 @@ packages: license_family: GPL size: 1925113 timestamp: 1771754008607 +- conda: https://conda.anaconda.org/conda-forge/linux-64/fftw-3.3.11-mpi_openmpi_h76e6d66_0.conda + sha256: 0949915580511f07e46d5509d8d16e7ae52910c426aa026dfba51cd188f0d93f + md5: 1f27b20b2c508b341d2f2fffc038318b + depends: + - __glibc >=2.17,<3.0.a0 + - libgcc >=14 + - libgfortran + - libgfortran5 >=14.3.0 + - libstdcxx >=14 + - openmpi >=5.0.10,<6.0a0 + license: GPL-2.0-or-later + license_family: GPL + size: 2243005 + timestamp: 1776781947588 - conda: https://conda.anaconda.org/conda-forge/linux-aarch64/fftw-3.3.10-mpi_openmpi_h79548e3_12.conda sha256: da93aca35dfc28cadf02d952c2e2edbbc82e13e6503058f525eda3d92f41cd3f md5: 862a16425e14f102cf30818160328013 @@ -6496,6 +6510,19 @@ packages: license_family: GPL size: 1446023 timestamp: 1771753652128 +- conda: https://conda.anaconda.org/conda-forge/linux-aarch64/fftw-3.3.11-mpi_openmpi_h79548e3_0.conda + sha256: b209929f5c33756a3c84f8ef43507749b85be09ee0b3b975b5e0c2f3484e3e66 + md5: db9e364f3a2395debbbb5dbbdd6c7313 + depends: + - libgcc >=14 + - libgfortran + - libgfortran5 >=14.3.0 + - libstdcxx >=14 + - openmpi >=5.0.10,<6.0a0 + license: GPL-2.0-or-later + license_family: GPL + size: 1765261 + timestamp: 1776782018274 - conda: https://conda.anaconda.org/conda-forge/osx-64/fftw-3.3.10-mpi_openmpi_h63b4274_12.conda sha256: e5dbf27576c5773034560395e37220b95b4b279ed037da2a9af7d36981cfbedf md5: 6e7cd3e40705a769a1e1af286b668f70 @@ -6523,6 +6550,20 @@ packages: license_family: GPL size: 1807039 timestamp: 1771754664178 +- conda: https://conda.anaconda.org/conda-forge/osx-64/fftw-3.3.11-mpi_openmpi_h63b4274_0.conda + sha256: be9d79fe2fa453c6ce0b83a5b2c809c7e9a3bdf6af4b5a984b72add962331bca + md5: cd9442c07a68a171acc0c3eec57b0b85 + depends: + - __osx >=11.0 + - libcxx >=19 + - libgfortran + - libgfortran5 >=14.3.0 + - llvm-openmp >=19.1.7 + - openmpi >=5.0.10,<6.0a0 + license: GPL-2.0-or-later + license_family: GPL + size: 1861341 + timestamp: 1776783990081 - conda: https://conda.anaconda.org/conda-forge/osx-arm64/fftw-3.3.10-mpi_openmpi_h83537c7_12.conda sha256: aff483eea254aa370c76eec87dd19f981c791a0824dec9c2f0e01881d95e3d5e md5: 96b7104aadf94ad19b5592655da364ce @@ -6550,6 +6591,20 @@ packages: license_family: GPL size: 757377 timestamp: 1771753896425 +- conda: https://conda.anaconda.org/conda-forge/osx-arm64/fftw-3.3.11-mpi_openmpi_h83537c7_0.conda + sha256: 31353fa1ad22a539b788347687e9a93e40bb44c8653733edfe0de310d6fabced + md5: 3ce1481c6d215e40ade8e91265a37d54 + depends: + - __osx >=11.0 + - libcxx >=19 + - libgfortran + - libgfortran5 >=14.3.0 + - llvm-openmp >=19.1.7 + - openmpi >=5.0.10,<6.0a0 + license: GPL-2.0-or-later + license_family: GPL + size: 782914 + timestamp: 1776782206460 - conda: https://conda.anaconda.org/conda-forge/noarch/filelock-3.29.0-pyhd8ed1ab_0.conda sha256: 6b471a18372bbd52bdf32fc965f71de3bc1b5219418b8e6b3875a67a7b08c483 md5: 8fa8358d022a3a9bd101384a808044c6 From 5080c31fe4b555b1d35f372f382f63b4ea1bee93 Mon Sep 17 00:00:00 2001 From: Alex Hocks Date: Wed, 6 May 2026 19:38:48 +0200 Subject: [PATCH 5/6] apply requested changes --- include/reader.h | 12 ++- pyfans/micro.cpp | 31 ++++++- pyfans/micro.hpp | 6 +- src/main.cpp | 2 +- src/reader.cpp | 214 ++++++++++++++++++++++------------------------- 5 files changed, 142 insertions(+), 123 deletions(-) diff --git a/include/reader.h b/include/reader.h index eab42d1..6a787d2 100644 --- a/include/reader.h +++ b/include/reader.h @@ -17,12 +17,14 @@ class Reader { public: // Default constructor Reader() = default; + Reader(const MPI_Comm &comm); // Destructor to free allocated memory ~Reader(); - // MPI controlls - bool force_single_rank; + // MPI + int world_rank; + int world_size; MPI_Comm communicator; // contents of input file: @@ -57,9 +59,6 @@ class Reader { unsigned short *ms{nullptr}; // Micro-structure bool is_zyx = true; - int world_rank; - int world_size; - ptrdiff_t alloc_local; ptrdiff_t local_n0; ptrdiff_t local_0_start; // this is the x-value of the start point, not the index in the array @@ -67,8 +66,7 @@ class Reader { ptrdiff_t local_1_start; // void Setup(ptrdiff_t howmany); - void ReadInputFile(char input_fn[]); - void ReadJson(const json &j); + void ReadInputFile(const std::string &input_fn); void ReadMS(int hm); void ComputeVolumeFractions(); // void ReadHDF5(char file_name[], char dset_name[]); diff --git a/pyfans/micro.cpp b/pyfans/micro.cpp index b9f3636..2e9f7b9 100644 --- a/pyfans/micro.cpp +++ b/pyfans/micro.cpp @@ -24,11 +24,40 @@ py::array_t merge_arrays(py::array_t array1, py::array_t return result; } -MicroSimulation::MicroSimulation(int sim_id, char *input_file) +PyFANSConfig load_config(const std::string &file_path) +{ + PyFANSConfig config{}; + + try { + ifstream i(file_path); + json j; + i >> j; + + if (j.contains("no_mpi") && j["no_mpi"].get()) + config.disable_mpi = true; + // ... more entries + } catch (const std::exception &e) { + printf("ERROR trying to read config file '%s' for pyFANS: %s\n", file_path.c_str(), e.what()); + printf("Falling back to default values.\n"); + } + + return config; +} + +MicroSimulation::MicroSimulation(int sim_id, const std::string &input_file, const std::string &config_file) { // initialize fftw mpi fftw_mpi_init(); + const auto config = load_config(config_file); + + // Avoiding reader re-initialization due to unnecessary buffer copies + reader.communicator = MPI_COMM_WORLD; + if (config.disable_mpi) + reader.communicator = MPI_COMM_SELF; + MPI_Comm_rank(reader.communicator, &reader.world_rank); + MPI_Comm_size(reader.communicator, &reader.world_size); + // Input file name is hardcoded. TODO: Make it configurable reader.ReadInputFile(input_file); diff --git a/pyfans/micro.hpp b/pyfans/micro.hpp index b6ebc6c..2dbe418 100644 --- a/pyfans/micro.hpp +++ b/pyfans/micro.hpp @@ -19,7 +19,7 @@ namespace py = pybind11; class MicroSimulation { public: - MicroSimulation(int sim_id, char *input_file = "input.json"); + MicroSimulation(int sim_id, const std::string &input_file = "input.json", const std::string &config_file = "pyfans-config.json"); py::dict solve(py::dict macro_write_data, double dt); private: @@ -30,3 +30,7 @@ class MicroSimulation { Solver<3, 6> *solver; double pert_param = 1e-6; // scalar strain perturbation parameter }; + +struct PyFANSConfig { + bool disable_mpi = false; +}; diff --git a/src/main.cpp b/src/main.cpp index 1df26f5..4832fc6 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -60,7 +60,7 @@ int main(int argc, char *argv[]) MPI_Init(NULL, NULL); fftw_mpi_init(); - Reader reader; + Reader reader{MPI_COMM_WORLD}; reader.ReadInputFile(argv[1]); reader.OpenResultsFile(argv[2]); diff --git a/src/reader.cpp b/src/reader.cpp index 72e7d7a..32033c9 100644 --- a/src/reader.cpp +++ b/src/reader.cpp @@ -10,6 +10,13 @@ #include "H5FDmpi.h" #include "H5FDmpio.h" +Reader::Reader(const MPI_Comm &comm) + : communicator(comm) +{ + MPI_Comm_rank(communicator, &world_rank); + MPI_Comm_size(communicator, &world_size); +} + void Reader::ComputeVolumeFractions() { unsigned short local_max = 0; @@ -60,136 +67,119 @@ void Reader::ComputeVolumeFractions() } } -void Reader ::ReadInputFile(char input_fn[]) +void Reader ::ReadInputFile(const std::string &input_fn) { try { ifstream i(input_fn); json j; i >> j; - ReadJson(j); - } catch (const std::exception &e) { - fprintf(stderr, "ERROR trying to read input file '%s' for FANS: %s\n", input_fn, e.what()); - exit(10); - } -} - -void Reader::ReadJson(const json &j) -{ - inputJson = j; // Store complete input JSON for MaterialManager - - if (j.contains("no_mpi") && j["no_mpi"].get()) { - force_single_rank = true; - communicator = MPI_COMM_SELF; - } else { - force_single_rank = false; - communicator = MPI_COMM_WORLD; - } - - MPI_Comm_rank(communicator, &world_rank); - MPI_Comm_size(communicator, &world_size); - - microstructure = j["microstructure"]; - std::snprintf(ms_filename, sizeof(ms_filename), "%s", microstructure["filepath"].get().c_str()); - // dataset name handling - const auto tmp_str = microstructure["datasetname"].get(); - if (tmp_str.empty()) - throw std::invalid_argument("datasetname must not be empty and must refer to a valid HDF5 path"); - // Ensure absolute HDF5 path, leading slash - std::snprintf(ms_datasetname, sizeof(ms_datasetname), "%s%s", tmp_str.front() == '/' ? "" : "/", tmp_str.c_str()); - L = microstructure["L"].get>(); - - if (j.contains("results_prefix")) { - std::snprintf(results_prefix, sizeof(results_prefix), "%s", j["results_prefix"].get().c_str()); - } else { - strcpy(results_prefix, ""); - } + inputJson = j; // Store complete input JSON for MaterialManager + microstructure = j["microstructure"]; + std::snprintf(ms_filename, sizeof(ms_filename), "%s", microstructure["filepath"].get().c_str()); + // dataset name handling + const auto tmp_str = microstructure["datasetname"].get(); + if (tmp_str.empty()) + throw std::invalid_argument("datasetname must not be empty and must refer to a valid HDF5 path"); + // Ensure absolute HDF5 path, leading slash + std::snprintf(ms_datasetname, sizeof(ms_datasetname), "%s%s", tmp_str.front() == '/' ? "" : "/", tmp_str.c_str()); + L = microstructure["L"].get>(); + + if (j.contains("results_prefix")) { + std::snprintf(results_prefix, sizeof(results_prefix), "%s", j["results_prefix"].get().c_str()); + } else { + strcpy(results_prefix, ""); + } - // Construct dataset_name as "_results/" - std::snprintf(dataset_name, sizeof(dataset_name), "%s_results/%s", ms_datasetname, results_prefix); + // Construct dataset_name as "_results/" + std::snprintf(dataset_name, sizeof(dataset_name), "%s_results/%s", ms_datasetname, results_prefix); - errorParameters = j["error_parameters"]; - TOL = errorParameters["tolerance"].get(); - n_it = j["n_it"].get(); + errorParameters = j["error_parameters"]; + TOL = errorParameters["tolerance"].get(); + n_it = j["n_it"].get(); - extrapolate_displacement = j.value("extrapolate_displacement", extrapolate_displacement); + extrapolate_displacement = j.value("extrapolate_displacement", extrapolate_displacement); - if (j.contains("linesearch_parameters")) { - ls_max_iter = j["linesearch_parameters"].value("max_iter", ls_max_iter); - ls_tol = j["linesearch_parameters"].value("tol", ls_tol); - if (ls_max_iter < 1 || ls_tol <= 0.0) - throw std::invalid_argument("linesearch_parameters: max_iter >= 1 and tol > 0 required"); - } + if (j.contains("linesearch_parameters")) { + ls_max_iter = j["linesearch_parameters"].value("max_iter", ls_max_iter); + ls_tol = j["linesearch_parameters"].value("tol", ls_tol); + if (ls_max_iter < 1 || ls_tol <= 0.0) + throw std::invalid_argument("linesearch_parameters: max_iter >= 1 and tol > 0 required"); + } - problemType = j["problem_type"].get(); - method = j["method"].get(); + problemType = j["problem_type"].get(); + method = j["method"].get(); - // Parse strain_type (optional, defaults to "small") - if (j.contains("strain_type")) { - strain_type = j["strain_type"].get(); - if (strain_type != "small" && strain_type != "large") { - throw std::invalid_argument("strain_type must be either 'small' or 'large'"); + // Parse strain_type (optional, defaults to "small") + if (j.contains("strain_type")) { + strain_type = j["strain_type"].get(); + if (strain_type != "small" && strain_type != "large") { + throw std::invalid_argument("strain_type must be either 'small' or 'large'"); + } + } else { + strain_type = "small"; // Default to small strain } - } else { - strain_type = "small"; // Default to small strain - } - // Parse FE_type (optional, defaults to "HEX8") - if (j.contains("FE_type")) { - FE_type = j["FE_type"].get(); - if (FE_type != "HEX8" && FE_type != "HEX8R" && FE_type != "BBAR") { - throw std::invalid_argument("FE_type must be one of: 'HEX8', 'HEX8R', or 'BBAR'"); + // Parse FE_type (optional, defaults to "HEX8") + if (j.contains("FE_type")) { + FE_type = j["FE_type"].get(); + if (FE_type != "HEX8" && FE_type != "HEX8R" && FE_type != "BBAR") { + throw std::invalid_argument("FE_type must be one of: 'HEX8', 'HEX8R', or 'BBAR'"); + } + } else { + FE_type = "HEX8"; // Default to full integration } - } else { - FE_type = "HEX8"; // Default to full integration - } - resultsToWrite = j["results"].get>(); // Read the results_to_write field + resultsToWrite = j["results"].get>(); // Read the results_to_write field - load_cases.clear(); - const auto &ml = j["macroscale_loading"]; - if (!ml.is_array()) - throw std::runtime_error("macroscale_loading must be an array"); + load_cases.clear(); + const auto &ml = j["macroscale_loading"]; + if (!ml.is_array()) + throw std::runtime_error("macroscale_loading must be an array"); - // Determine the size of loading vector based on problem type and strain formulation - int n_str; - if (problemType == "thermal") { - n_str = 3; // Temperature gradient components - } else if (strain_type == "large") { - n_str = 9; // Deformation gradient components (F11, F12, F13, F21, F22, F23, F31, F32, F33) - } else { - n_str = 6; // Small strain components (eps11, eps22, eps33, eps12, eps13, eps23) - } + // Determine the size of loading vector based on problem type and strain formulation + int n_str; + if (problemType == "thermal") { + n_str = 3; // Temperature gradient components + } else if (strain_type == "large") { + n_str = 9; // Deformation gradient components (F11, F12, F13, F21, F22, F23, F31, F32, F33) + } else { + n_str = 6; // Small strain components (eps11, eps22, eps33, eps12, eps13, eps23) + } - for (const auto &entry : ml) { - LoadCase lc; - if (entry.is_array()) { // ---------- legacy pure-strain ---------- - lc.mixed = false; - lc.g0_path = entry.get>>(); - lc.n_steps = lc.g0_path.size(); - if (lc.g0_path[0].size() != static_cast(n_str)) - throw std::invalid_argument("Invalid length of loading vector: expected " + - std::to_string(n_str) + " components but got " + - std::to_string(lc.g0_path[0].size())); - } else { // ---------- mixed BC object ------------ - lc.mixed = true; - lc.mbc = MixedBC::from_json(entry, n_str); - lc.n_steps = lc.mbc.F_E_path.rows(); + for (const auto &entry : ml) { + LoadCase lc; + if (entry.is_array()) { // ---------- legacy pure-strain ---------- + lc.mixed = false; + lc.g0_path = entry.get>>(); + lc.n_steps = lc.g0_path.size(); + if (lc.g0_path[0].size() != static_cast(n_str)) + throw std::invalid_argument("Invalid length of loading vector: expected " + + std::to_string(n_str) + " components but got " + + std::to_string(lc.g0_path[0].size())); + } else { // ---------- mixed BC object ------------ + lc.mixed = true; + lc.mbc = MixedBC::from_json(entry, n_str); + lc.n_steps = lc.mbc.F_E_path.rows(); + } + load_cases.push_back(std::move(lc)); } - load_cases.push_back(std::move(lc)); - } - if (world_rank == 0) { - printf("# microstructure file name: \t '%s'\n", ms_filename); - printf("# microstructure dataset name: \t '%s'\n", ms_datasetname); - printf("# strain type: \t %s\n", strain_type.c_str()); - printf("# problem type: \t %s\n", problemType.c_str()); - printf("# FE type: \t %s\n", FE_type.c_str()); - printf( - "# FANS error measure: \t %s %s error \n", - errorParameters["type"].get().c_str(), - errorParameters["measure"].get().c_str()); - printf("# FANS Tolerance: \t %10.5e\n", errorParameters["tolerance"].get()); - printf("# Max iterations: \t %6i\n", n_it); + if (world_rank == 0) { + printf("# microstructure file name: \t '%s'\n", ms_filename); + printf("# microstructure dataset name: \t '%s'\n", ms_datasetname); + printf("# strain type: \t %s\n", strain_type.c_str()); + printf("# problem type: \t %s\n", problemType.c_str()); + printf("# FE type: \t %s\n", FE_type.c_str()); + printf( + "# FANS error measure: \t %s %s error \n", + errorParameters["type"].get().c_str(), + errorParameters["measure"].get().c_str()); + printf("# FANS Tolerance: \t %10.5e\n", errorParameters["tolerance"].get()); + printf("# Max iterations: \t %6i\n", n_it); + } + } catch (const std::exception &e) { + fprintf(stderr, "ERROR trying to read input file '%s' for FANS: %s\n", input_fn.c_str(), e.what()); + exit(10); } } @@ -247,9 +237,7 @@ void Reader ::ReadMS(int hm) hid_t plist_id; /* property list identifier */ herr_t status; - MPI_Comm_rank(communicator, &world_rank); - MPI_Comm_size(communicator, &world_size); - MPI_Info info = MPI_INFO_NULL; + // MPI_Info info = MPI_INFO_NULL; // Set up file access property list with parallel I/O access plist_id = H5Pcreate(H5P_FILE_ACCESS); From 50bb2e16b13bd9d52e4441820aa60eeb9f4410c7 Mon Sep 17 00:00:00 2001 From: sanathkeshav Date: Thu, 7 May 2026 10:01:07 +0200 Subject: [PATCH 6/6] minor changes --- CHANGELOG.md | 2 +- README.md | 2 -- include/reader.h | 9 ++++----- src/reader.cpp | 5 ++--- 4 files changed, 7 insertions(+), 11 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index ef87b7c..2aa64b8 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,7 +2,7 @@ ## latest -- Added switching of MPI communicator [#139](https://github.com/DataAnalyticsEngineering/FANS/pull/139) +- Added MPI communicator abstraction [#139](https://github.com/DataAnalyticsEngineering/FANS/pull/139) ## v0.6.2 diff --git a/README.md b/README.md index 0a63512..90b6a6a 100644 --- a/README.md +++ b/README.md @@ -292,7 +292,6 @@ FANS requires a JSON input file specifying the problem parameters. Example input "tolerance": 1e-10 }, "n_it": 100, -"no_mpi": true, ``` - `FE_type`: This specifies the type of finite element to be used. Common options include: @@ -305,7 +304,6 @@ FANS requires a JSON input file specifying the problem parameters. Example input - `type`: Defines the type of error measurement. Options are `absolute` or `relative`. - `tolerance`: Sets the tolerance level for the solver, defining the convergence criterion based on the chosen error measure. The solver iterates until the solution meets this tolerance. - `n_it`: Specifies the maximum number of iterations allowed for the FANS solver. -- `no_mpi`: Turns off parallel MPI computation and forces the usage of `MPI_COMM_SELF` ### Macroscale loading conditions diff --git a/include/reader.h b/include/reader.h index 6a787d2..75a504b 100644 --- a/include/reader.h +++ b/include/reader.h @@ -22,11 +22,6 @@ class Reader { // Destructor to free allocated memory ~Reader(); - // MPI - int world_rank; - int world_size; - MPI_Comm communicator; - // contents of input file: char ms_filename[4096]{}; // Name of Micro-structure hdf5 file char ms_datasetname[4096]{}; // Absolute path of Micro-structure in hdf5 file @@ -59,6 +54,10 @@ class Reader { unsigned short *ms{nullptr}; // Micro-structure bool is_zyx = true; + int world_rank; + int world_size; + MPI_Comm communicator; + ptrdiff_t alloc_local; ptrdiff_t local_n0; ptrdiff_t local_0_start; // this is the x-value of the start point, not the index in the array diff --git a/src/reader.cpp b/src/reader.cpp index 32033c9..ea7dd7f 100644 --- a/src/reader.cpp +++ b/src/reader.cpp @@ -73,7 +73,8 @@ void Reader ::ReadInputFile(const std::string &input_fn) ifstream i(input_fn); json j; i >> j; - inputJson = j; // Store complete input JSON for MaterialManager + inputJson = j; // Store complete input JSON for MaterialManager + microstructure = j["microstructure"]; std::snprintf(ms_filename, sizeof(ms_filename), "%s", microstructure["filepath"].get().c_str()); // dataset name handling @@ -237,8 +238,6 @@ void Reader ::ReadMS(int hm) hid_t plist_id; /* property list identifier */ herr_t status; - // MPI_Info info = MPI_INFO_NULL; - // Set up file access property list with parallel I/O access plist_id = H5Pcreate(H5P_FILE_ACCESS); // H5Pset_fapl_mpio(plist_id, MPI_COMM_WORLD, info); // "set File Access Property List"