Git Product home page Git Product logo

openpfc's Introduction

OpenPFC

Screenshot of OpenPFC simulation result

Phase field crystal (PFC) is a semi-atomistic technique, containing atomic resolution information of crystalline structures while operating on diffusive time scales. PFC can simulate solidification and elastic-plastic material response, coupled with a wide range of phenomena, including formation and co-evolution of microstructural defects such as dislocations and stacking faults, voids, defect formation in epitaxial growth, displacive phase transitions, and electromigration.

The image above shows a simulation of a rapidly solidifying tungsten block approximately 50 x 100 x 200 nm in size, using MovingBC boundary conditions. The rightmost section depicts the pure atomic structure of the tungsten, the middle section highlights the surface of the entire object and the leftmost section provides a transparent view of the surface, revealing the lattice defects that formed during the simulation. This visualization aids in understanding the atomic arrangement, surface features, and internal defects of the tungsten block.

Scalability and Performance

OpenPFC is an open-source framework for high-performance 3D phase field crystal simulations. It is designed to scale up from a single laptop to exascale class supercomputers. OpenPFC has successfully been used to simulate a domain of size 8192 x 8192 x 4096 on CSC Mahti. 200 computing nodes were used, where each node contained 128 cores, thus total of 25600 cores were used. During the simulation, 25 TB of memory was utilized. The central part of the solver is the Fast Fourier Transform with time complexity of O(N log N), and there are no known limiting bottlenecks, why larger models could not be calculated as well.

OpenPFC scalability

The graph above demonstrates the remarkable scalability and performance of the simulation framework. In the strong scaling analysis (panel a), the step time significantly decreases as the number of cores increases for various grid sizes, from 256³ to 8192³. This indicates efficient parallelization, though the rate of decrease diminishes at higher core counts.

In the weak scaling analysis (panel b), the step time remains relatively constant as the grid size increases while maintaining a fixed number of voxels per core. This stability illustrates excellent weak scaling performance, highlighting the framework's capability to efficiently manage larger problems by proportionally increasing computational resources. The right Y-axis projects the number of time steps calculable in a week, emphasizing the framework's suitability for extensive simulations on supercomputers. Notably, these simulations were conducted using the LUMI supercomputer, further showcasing the framework's capability to leverage top-tier computational resources for high-performance simulations.

T. Pinomaa, J. Aho, J. Suviranta, P. Jreidini, N. Provatas, and A. Laukkanen, “OpenPFC: an open-source framework for high performance 3D phase field crystal simulations”, Modelling Simul. Mater. Sci. Eng., Feb. 2024, doi: 10.1088/1361-651X/ad269e. (link)

Documentation

The project documentation can be found from https://vtt-propertune.github.io/OpenPFC/dev/.

Features

  • scales up to tens of thousands of cores, demonstrably
  • modern c++17 header-only framework, easy to use

Installing

Using singularity

  • Todo

Compiling from source

Requirements:

  • Compiler supporting C++17 standard: C++17 features are available since GCC 5. Check your version number with g++ --version. The default compiler might be relatively old, and a more recent version needs to be loaded with module load gcc. Do not try to compile with GCC 4.8.5. It will not work. At least GCC versions 9.4.0 (coming with Ubuntu 20.04) and 11.2 are working.
  • CMake: Version 3.15 or later should be used. Your system may already contain CMake, but if not, it can most likely be installed with the package manager.
  • OpenMPI: All recent versions should work. Tested with OpenMPI version 2.1.3. Again, you might need to load proper OpenMPI version with module load openmpi/2.1.3, for instance. Additionally, if CMake is not able to find proper OpenMPI installation, assistance might be needed by setting MPI_ROOT, e.g. export MPI_ROOT=/share/apps/OpenMPI/2.1.3.
  • FFTW: Probably all versions will work. Tested with FFTW versions 3.3.2 and 3.3.10. Again, CMake might need some assistance to find the libraries, which can be controlled with environment variable FFTW_ROOT. Depending how FFTW is installed to system, it might be in non-standard location and module load fftw is needed. You can use commands like whereis fftw or ldconfig -p | grep fftw to locate your FFTW installation, if needed.
  • Niels Lohmann's JSON for Modern C++ library: All recent versions should work. Tested with version 3.11.2. If you do not have the JSON library installed, CMake for OpenPFC will download the library for you.
  • HeFFTe: All recent versions should work. Tested with version 2.3.0.

Typically in clusters, these are already installed and can be loaded with an on-liner

module load gcc openmpi fftw

For local Linux machines (or WSL2), packages usually can be installed from repositories, e.g. in the case of Ubuntu, the following should work:

sudo apt-get install -y gcc openmpi fftw

Some OpenPFC applications use JSON files to provide initial data for simulations. In principle, applications can also be built to receive initial data in other ways, but as a widely known file format, we recommend using JSON. The choice for the JSON package is JSON for Modern C++. There exist packages for certain Linux distributions (nlohmann-json3-dev for Ubuntu, json-devel for Centos) for easy installation. If the system-wide installation is not found, the library is downloaded from GitHub during the configuration.

The last and most important dependency to use OpenPFC is HeFFTe, which is our choice for parallel FFT implementation. The instructions to install HeFFTe can be found from here. HeFFTe can be downloaded from https://github.com/icl-utk-edu/heffte. Your typical workflow to install HeFFTe would be something like this:

cmake -S heffte-2.4.0-src -B heffte-2.4.0-build \
    -DCMAKE_INSTALL_PREFIX=/opt/heffte/2.4.0 \
    -DCMAKE_BUILD_TYPE=Release -D Heffte_ENABLE_FFTW=ON
cmake --build heffte-2.4.0-build
cmake --install heffte-2.4.0-build

If HeFFTe is installed in some non-standard location, CMake is unable to find it when configuring OpenPFC. To overcome this problem, the install path of HeFFTe can be set into the environment variable CMAKE_PREFIX_PATH. For example, if HeFFe is installed to $HOME/opt/heffte/2.3, the following is making CMake to find HeFFTe successfully:

export CMAKE_PREFIX_PATH=$HOME/opt/heffte/2.3:$CMAKE_PREFIX_PATH

During the configuration, OpenPFC prefers local installations, thus if HeFFTe is already installed and found, it will be used. For convenience, there is a fallback method to fetch HeFFTe sources from the internet and build it concurrently with OpenPFC. In general, however, it is better to build and install programs one at a time. So, make sure you have HeFFTe installed and working on your system before continuing.

OpenPFC uses cmake to automate software building. First, the source code must be downloaded to some appropriate place. Head to the releases page and pick the newest release and unzip it somewhere. Alternatively, if you are planning to develop the project itself or are just interested in the bleeding-edge features, you might be interested in cloning the repository to your local machine. A GitHub account is needed to clone the project.

git clone https://github.com/VTT-ProperTune/OpenPFC.git
# git clone [email protected]:VTT-ProperTune/OpenPFC.git  # if you prefer ssh instead
cd OpenPFC

The next step is to configure the project. One might consider at least setting an option CMAKE_BUILD_TYPE to Debug or Release. For large-scale simulations, make sure to use Release as it turns on compiler optimizations.

cmake -DCMAKE_BUILD_TYPE=Release -S . -B build

Keep in mind, that the configuration will download HeFFTe if the local installation is not found. To use local installation instead, add HeFFTe path to the environment variable CMAKE_PREFIX_PATH or add Heffte_DIR option to point where HeFFTe configuration files are installed. A typical configuration command in a cluster environment is something like

module load gcc openmpi fftw
export CMAKE_PREFIX_PATH=$HOME/opt/heffte/2.3:$CMAKE_PREFIX_PATH
cmake -DCMAKE_BUILD_TYPE=Release \
      -DCMAKE_INSTALL_PREFIX=$HOME/opt/openpfc \
      -S . -B build

Then, building can be done with the command cmake --build build. After the build finishes, one should find example codes from ./build/examples and apps from ./build/apps. Installation to a path defined by CMAKE_INSTALL_PREFIX can be done with cmake --install build.

Structure of the application

classDiagram
  App~Model~ --> Simulator
  Simulator --> Model
  Simulator --> Time
  Simulator --> FieldModifier
  Simulator --> ResultsWriter
  Model --> FFT
  Model --> RealField
  Model --> ComplexField
  FFT --> Decomposition
  Decomposition --> World
Loading

The OpenPFC framework aims to simplify the development of highly scalable applications for solving partial differential equations using spectral methods. It provides a modular application structure that users can customize by inheriting and overriding specific classes. When examining the class diagram from bottom to top, we first encounter classes such as World, Decomposition, and FFT. These classes form a low-level layer responsible for domain decomposition and performing FFT using the HeFFTe library. These details might not be of general interest from an implementation perspective, except for the framework developers themselves.

Next, we have classes such as Model, FieldModifier, and ResultsWriter. The Model class is of particular interest as it describes the physics of the model, including the partial differential equation (PDE) itself. Inside the Model class, there is a function called step that needs to be overridden. Currently, users are free to choose whichever time integration method they are comfortable with. However, in the future, we may abstract the time integration method away from the model and create a separate class to approach the problem using "The Method of Lines" view. The Model class consists of one or several different "fields" which can be real or complex-valued. These fields are updated during time stepping. The FieldModifier class does exactly what the name implies – it modifies these fields. In more detail, initial and boundary conditions serve as field modifiers and are often also of interest, although some already implemented ones exist. Lastly, we should mention the ResultsWriter, which implements a way to store results during certain periods. We have some existing implementations such as raw binary format and vti format, but nothing is preventing us from implementing, for example, the storage of results in hdf5 format, which is currently under planning.

In the third level, we have the Simulator, which assembles and runs the actual simulation. It's a simple container-like class that calls lower-level objects in a stepper to ensure that everything is called in time. Typically, there should be no need to override this, but it is still possible to do so.

The top level is App, which handles the user interface. Since the simulations are usually run on supercomputers, we don't have anything fancy like a graphical user interface or interactive control of the simulation. Instead, we input user parameters in an input file, preferably in JSON format. After reading the model parameters, the simulator starts. This type of user interface is very basic, but it works well in high-performance computing (HPC) environments where there are no displays available. Typically, a batch script (e.g. Slurm) is created to run the application in the chosen HPC environment's queue.

Getting started

OpenPFC is a software framework. It doesn't give you ready-made solutions, but a platform on which you can start building your own scalable PFC code. We will familiarize ourselves with the construction of the model with the help of a simple diffusion model in a later stage of the documentation. However, let's give a tip already at this stage, how to start the development work effectively. Our "hello world" code is as follows:

#include <iostream>
#include <openpfc/openpfc.hpp>

using namespace std;
using namespace pfc;

int main() {
  World world({32, 32, 32});
  cout << world << endl;
}

To compile, CMakeLists.txt is needed. Minimal CMakeLists.txt is:

cmake_minimum_required(VERSION 3.15)
project(helloworld)
find_package(OpenPFC REQUIRED)
add_executable(main main.cpp)
target_link_libraries(main OpenPFC)

With the help of CMakeLists.txt, build and compilation of the application is straightforward:

cmake -S . -B build
cmake --build build
./build/main

There are also some examples in examples directory, which can be used as a base for your codes.

Example: Cahill-Hilliard equation

The Cahn-Hilliard equation is a fundamental model in materials science used to describe the phase separation process in binary mixtures. It models how the concentration field evolves over time to minimize the free energy of the system. The equation is particularly useful in understanding the dynamics of spinodal decomposition and coarsening processes.

Spectral methods, combined with the Fast Fourier Transform (FFT), are highly efficient for solving partial differential equations (PDEs) like the Cahn-Hilliard equation. The FFT allows us to transform differential operators into algebraic ones in the frequency domain, significantly simplifying the computation. This approach is particularly advantageous for problems involving periodic boundary conditions and large-scale simulations, where the efficiency and accuracy of the FFT are paramount.

Exponential time integration is well-suited for stiff PDEs like the Cahn-Hilliard equation. Traditional explicit methods require very small time steps to maintain stability, which can be computationally expensive. Exponential integrators, however, handle the stiff linear part of the equation exactly, allowing for larger time steps without sacrificing stability. This makes the integration process more efficient and stable, especially for long-term simulations.

Starting with the Cahn-Hilliard equation:

$$ \frac{\partial c}{\partial t} = D \nabla^{2} \left( c^{3} - c - \gamma \nabla^{2} c \right) $$

Fourier Transform

Applying the Fourier transform to the equation converts the spatial differential operators into algebraic forms:

$$ \frac{\partial \hat{c}}{\partial t} = D \left[ -k^2 \left( \hat{c^3} - \hat{c} - \gamma (-k^2 \hat{c}) \right) \right] $$

Simplifying the right-hand side:

$$ \frac{\partial \hat{c}}{\partial t} = D \left[ -k^2 \hat{c^3} + k^2 \hat{c} + \gamma k^4 \hat{c} \right] $$

$$ \frac{\partial \hat{c}}{\partial t} = D \left[ -k^2 \hat{c^3} + (k^2 + \gamma k^4) \hat{c} \right] $$

Discretization in Time

Using an exponential integrator, we handle the linear part exactly and integrate the non-linear part explicitly:

$$ \hat{c}(t + \Delta t) = \exp(DL \Delta t) \hat{c}(t) + \int_t^{t + \Delta t} \exp(DL (t + \Delta t - s)) (-Dk^2 \hat{c^3}(s)) , \mathrm{d}s $$

Here, $L = k^2 + \gamma k^4$.

Assuming $\hat{c}$ is approximately constant over the small interval $\Delta t$, we approximate the integral:

$$ \hat{c}(t + \Delta t) \approx \exp(D (k^2 + \gamma k^4) \Delta t) \hat{c}(t) + \left( \frac{ \exp(D (k^2 + \gamma k^4) \Delta t) - 1 }{D (k^2 + \gamma k^4)} \right) (-Dk^2 \hat{c^3}(t)) $$

Final Time-Stepping Formula

Combining the terms, we obtain the discrete update rule for $\hat{c}$:

$$ \hat{c}_{n+1} = \exp(D (k^2 + \gamma k^4) \Delta t) \hat{c}_n + \frac{\exp(D (k^2 + \gamma k^4) \Delta t) - 1}{D (k^2 + \gamma k^4)} (-Dk^2 \hat{c^3}_n) $$

Simplify the coefficient for the non-linear term:

$$ \hat{c}_{n+1} = \exp(D (k^2 + \gamma k^4) \Delta t) \hat{c}_n - \frac{\exp(D (k^2 + \gamma k^4) \Delta t) - 1}{k^2 + \gamma k^4} k^2 \hat{c^3}_n $$

Linear and Non-Linear Operators

The linear and non-linear operators can be defined as follows:

$$ \begin{align} \text{opL} &= \exp(D (k^2 + \gamma k^4) \Delta t) \\ \text{opN} &= \frac{\exp(D (k^2 + \gamma k^4) \Delta t) - 1}{k^2 + \gamma k^4} k^2 \end{align} $$

These operators are used to update the concentration field $c$ in the Fourier domain efficiently, leveraging the FFT for computational efficiency. This method allows for stable and accurate integration of the Cahn-Hilliard equation over time, making it suitable for large-scale simulations of phase separation processes.

Below is the code snippet for the Cahn-Hilliard model in OpenPFC:

class CahnHilliard : public Model {
private:
  std::vector<double> opL, opN, c;             // Define operators and field c
  std::vector<std::complex<double>> c_F, c_NF; // Define (complex) psi
  double gamma = 1.0e-2;                       // Surface tension
  double D = 1.0;                              // Diffusion coefficient

public:
  void initialize(double dt) override {
    FFT &fft = get_fft();
    const Decomposition &decomp = get_decomposition();

    // Allocate space for the main variable and it's fourier transform
    c.resize(fft.size_inbox());
    c_F.resize(fft.size_outbox());
    c_NF.resize(fft.size_outbox());
    opL.resize(fft.size_outbox());
    opN.resize(fft.size_outbox());
    add_real_field("concentration", c);

    // prepare operators
    World w = get_world();
    std::array<int, 3> o_low = decomp.outbox.low;
    std::array<int, 3> o_high = decomp.outbox.high;
    size_t idx = 0;
    double pi = std::atan(1.0) * 4.0;
    double fx = 2.0 * pi / (w.dx * w.Lx);
    double fy = 2.0 * pi / (w.dy * w.Ly);
    double fz = 2.0 * pi / (w.dz * w.Lz);
    for (int k = o_low[2]; k <= o_high[2]; k++) {
      for (int j = o_low[1]; j <= o_high[1]; j++) {
        for (int i = o_low[0]; i <= o_high[0]; i++) {
          // Laplacian operator -k^2
          double ki = (i <= w.Lx / 2) ? i * fx : (i - w.Lx) * fx;
          double kj = (j <= w.Ly / 2) ? j * fy : (j - w.Ly) * fy;
          double kk = (k <= w.Lz / 2) ? k * fz : (k - w.Lz) * fz;
          double kLap = -(ki * ki + kj * kj + kk * kk);
          double L = kLap * (-D - D * gamma * kLap);
          opL[idx] = std::exp(L * dt);
          opN[idx] = (L != 0.0) ? (opL[idx] - 1.0) / L * kLap : 0.0;
          idx++;
        }
      }
    }
  }

  void step(double) override {
    // Calculate cₙ₊₁ = opL * cₙ + opN * cₙ³
    FFT &fft = get_fft();
    fft.forward(c, c_F);
    for (auto &elem : c) elem = D * elem * elem * elem;
    fft.forward(c, c_NF);
    for (size_t i = 0; i < c_F.size(); i++) {
      c_F[i] = opL[i] * c_F[i] + opN[i] * c_NF[i];
    }
    fft.backward(c_F, c);
  }
};

Cahn-Hilliard simulation

The full code can be found from examples.

Troubleshooting and debugging

Here are some common problems and their solutions.

FindOpenPFC.cmake not found

During the configuration step (cmake -S. -B build), you might end up with the following error message:

CMake Error at CMakeLists.txt:3 (find_package):
By not providing "FindOpenPFC.cmake" in CMAKE_MODULE_PATH this project has
asked CMake to find a package configuration file provided by "OpenPFC", but
CMake did not find one.

The error message is trying to say the command in CMakeLists.txt (line 3) fails:

find_package(OpenPFC REQUIRED)  # <-- this is failing

The reason why this happens is that CMake is not able to find the package. By default, CMake finds packages by looking at a file which is called Find<package_name>.cmake from a couple of standard locations. For example, in Ubuntu, one of these locations is /usr/lib/cmake, where the files are installed when doing a global install of some package with root rights. When working with supercomputers, users, in general, don't have rights to make global installations, thus packages are almost always installed to some non-default locations. Thus, one needs to give some hints to CMake where the file could be found. This can be done (at least) in two different ways.

The first way is to set up an environment variable indicating any extra locations for the files. One option is to use CMAKE_PREFIX_PATH environment variable, like before. For example, if OpenPFC is installed to /opt/OpenPFC, one can give that information before starting the configuration:

export CMAKE_PREFIX_PATH=/opt/OpenPFC:$CMAKE_PREFIX_PATH
cmake -S . -B build
# rest of the things ...

Another option is to hardcode the choice inside the CMakeLists.txt file directly. Just keep in mind, that this option is not very portable as users tends to install software to several different locations and there is no any general rule on how it should be done. So, instead of defining CMAKE_PREFIX_PATH before doing configuration, the following change in CMakeLists.txt is equivalent:

cmake_minimum_required(VERSION 3.15)
project(helloworld)
# find_package(OpenPFC REQUIRED)                                     #  <-- Replace this command ...
find_package(OpenPFC REQUIRED PATHS /opt/OpenPFC/lib/cmake/OpenPFC)  #  <-- ... with this one
add_executable(main main.cpp)
target_link_libraries(main OpenPFC)

This way, CMake knows to search for necessary files from the path given above.

NaNs in the simulation

There might be various reasons why the simulation returns NaNs. Despite the reason, it usually makes sense to stop simulation as it doesn't do anything useful. OpenPFC does not currently have a built-in JSON validator, which would check that simulation parameters are valid. Thus, it is possible to give invalid parameters to the simulation, which might lead to NaNs. If some model parameters that should be defined are undefined and thus zero, there might be a zero division problem.

There is a schema file for the input file, which can be used to validate the JSON file using an external validator like check-jsonchema:

check-jsonschema --schemafile apps/schema.json input.json

OpenPFC implements NaN check, which is enabled by default when compiling with a debug build type:

cmake -DCMAKE_BUILD_TYPE=Debug -S . -B build

Another way to enable NaN check is to use compile option NAN_CHECK_ENABLED. In CMakeLists.txt, add the following line:

add_compile_definitions(NAN_CHECK_ENABLED)

Or, when configuring a project with CMake, the following is equivalent:

cmake -DNAN_CHECK_ENABLED=ON -S . -B build

Or another, quick and dirty solution might be to simply add the following to the source file:

#define NAN_CHECK_ENABLED

Then, at the code level, there's a macro CHECK_AND_ABORT_IF_NANS, which can be used to check if there are any NaNs in the simulation. The macro is defined in openpfc/utils/nancheck.hpp. This is a zero overhead when compiling with release build type. At the moment, a user must explicitly call the macro, but in the future, it might be called automatically in some situations. Example usage is (see also this example):

std::vector<double> psi = {1.0, 2.0, 3.0, 4.0, 5.0, 6.0};
CHECK_AND_ABORT_IF_NANS(psi);
psi[0] = std::numeric_limits<double>::quiet_NaN();
CHECK_AND_ABORT_IF_NANS(psi);

Citing

@article{pinomaa2024openpfc,
  title={OpenPFC: an open-source framework for high performance 3D phase field crystal simulations},
  author={Pinomaa, Tatu and Aho, Jukka and Suviranta, Jaarli and Jreidini, Paul and Provatas, Nikolaos and Laukkanen, Anssi},
  journal={Modelling and Simulation in Materials Science and Engineering},
  year={2024}
}

openpfc's People

Contributors

ahojukka5 avatar jskortel avatar

Stargazers

 avatar  avatar Tom Flint avatar Jitin avatar Mehrdad Yousefi avatar Trevor Keller avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar

openpfc's Issues

Tests do not compile

[  1%] Building CXX object _deps/catch2-build/src/CMakeFiles/Catch2.dir/catch2/benchmark/catch_chronometer.cpp.o
/home/juajukka/dev/OpenPFC-build/_deps/catch2-src/src/catch2/../catch2/benchmark/catch_clock.hpp:13:10: error: 'chrono' file not found [clang-diagnostic-error]
#include <chrono>
         ^~~~~~~~
/home/juajukka/dev/OpenPFC-build/_deps/catch2-src/src/catch2/benchmark/catch_chronometer.cpp:11:1: warning: nested namespaces can be concatenated [modernize-concat-nested-namespaces]
namespace Catch {
^~~~~~~~~~~~~~~~~
namespace Catch::Benchmark::Detail
/home/juajukka/dev/OpenPFC-build/_deps/catch2-src/src/catch2/benchmark/catch_chronometer.cpp:11:11: warning: '__llvm_libc' needs to be the outermost namespace [llvmlibc-implementation-in-namespace]
namespace Catch {
          ^
182 warnings and 1 error generated.
Error while processing /home/juajukka/dev/OpenPFC-build/_deps/catch2-src/src/catch2/benchmark/catch_chronometer.cpp.
Suppressed 180 warnings (180 in non-user code).
Use -header-filter=.* to display errors from all non-system headers. Use -system-headers to display errors from system headers as well.
Found compiler error(s).
gmake[2]: *** [_deps/catch2-build/src/CMakeFiles/Catch2.dir/build.make:76: _deps/catch2-build/src/CMakeFiles/Catch2.dir/catch2/benchmark/catch_chronometer.cpp.o] Error 1
gmake[1]: *** [CMakeFiles/Makefile2:1051: _deps/catch2-build/src/CMakeFiles/Catch2.dir/all] Error 2
gmake: *** [Makefile:136: all] Error 2

Implement VtkWriter

To make it easier open result files, we need VtkWriter implementation. It's actually already implemented in examples. The interface differs a bit from the earlier, so maybe some backward incompatible changes are needed to get it working. Writer uses basically the same idea than BinaryWriter, MPI_Write_all writes to binary from all processes, but now there is a small offset in the beginning of binary file and vti header is written to it. Unit test is needed.

Implement HdfWriter

There came an idea that results could be written to HDF5 format. The idea is good because the format supports compression. We could then write xml metadata format, thus having Xdmf file format, which opens easily using Paraview. I'm not aware how parallel writing to HDF5 works, but I guess is should be possible. The amount of data is so big that it's impossible to first move all data to a single node and use only one process to write data.

clang-tidy is not found

CMake Warning at CMakeLists.txt:40 (message):                                                                             
clang-tidy executable not found.  Please install clang-tidy and ensure it                                               
is in your system PATH. 

Be more informative what this means in practice. What is clang-tidy at all.

option(USE_CLANG_TIDY "Enable clang-tidy static code analysis" ON)

Actually, it would be nice to have option that if not explicitly spesified, CLANG_TIDY is ON when building Debug and OFF when building Release.

Fix CLANG_TIDY bug

if(ENABLE_CLANG_TIDY)
  set(CMAKE_CXX_CLANG_TIDY clang-tidy)
endif()

Fails if executable not found.

Add scalability results from LUMI

Demonstrate the scalability by log-log plot and include that to README.md. We have new tests ran on LUMI, up to 512 nodes, let's use them.

Restart is not working

Restart is not working, at least not for aluminum model. Fix it and write unit test to make sure it works.

Improvements to NaN checking

  1. Let's enable NaN checking somewhere in an automatic way if DCMAKE_BUILD_TYPE=Debug. It's well justified as in debug mode all optimizations are anyway disabled and performance doesn't matter.
  2. Let's add option to enable NaN checking also in production code using json input file. Some kind of "enable_nan_check": true.
  3. In documentation, create "debugging" page or section, describing some strategies how to debug a nonworking model. At least describe the use of abortIfNaNs. Mention also that issue #31 might be a good source of NaNs if input data is missing.

Put something to Example section

Let's put there the solution of some stiff PDE having both linear and nonlinear part. Let's try to make it as short as possible.

Support for 32-bit arithmetic

Use templates everywhere to support 32-bit arithmetic. This is probably needed when we are going towards GPU calculations.

Create COPYING file

There could be a separate COPYING file with the copyright transfer notice (the current LICENSE file content).

Install failed when using system-wide nlohmann_json headers

-- Up-to-date: /opt/openpfc/dev/include/openpfc/utils.hpp
-- Up-to-date: /opt/openpfc/dev/include/openpfc/multi_index.hpp
-- Up-to-date: /opt/openpfc/dev/include/openpfc/boundary_conditions
-- Up-to-date: /opt/openpfc/dev/include/openpfc/boundary_conditions/fixed_bc.hpp
-- Up-to-date: /opt/openpfc/dev/include/openpfc/boundary_conditions/moving_bc.hpp
CMake Error at build/cmake_install.cmake:75 (file):
  file INSTALL cannot find "/include/nlohmann": No such file or directory.

Conditionally install only if downloaded during configuration.

Validate json file when loading configuration

It's nice to have NaN checks:

NaNs detected on process 1 at /home/juajukka/dev/openpfc/apps/tungsten.cpp:165. Aborting MPI application.

However, it would be much nicer to validate json inputs so that all necessary things are certainly defined.

  1. If something non-critical is missing, give warning and use some meaninful default
  2. If something critical is missing, stop the program and describe what needs to be added

https://github.com/pboettch/json-schema-validator

Create singularity container

We should try to create a singularity container, containing everything ready to go. This would make it much easier to try out the code. A challenge is on how to create container such a way that it would use system wide MPI libraries so that we can go to HPC, but the first step would be just to install MPI libraries inside singularity container itself.

Clarify HeFFTe install instructions

Check where to download and link install instructions. If it's still unclear how to do it, maybe we should consider to contribute to the install instructions there as it's not the subject of this project.

Add option to restart moving boundary condition

Add option to output moving bc state to stdout or file. We need it to set initial state properly in order to make restart working.

In general, we should have a way to log things to files. Maybe search for existing logging solutions which works well for MPI applications and use one of those. There must exist some.

Create EasyBuild recipe

It should be compatible with LUMI so that user can easily install everything:

module load LUMI/23.09 partition/C
module load EasyBuild-user
eb OpenPFC-0.1.0-cpeCray-23.09-cray-fftw-3.3.10.5.eb -r

Add possibility to choose field for initial condition

    SeedGridFCC ic;
    ic.set_Nx(1);
    ic.set_Ny(2);
    ic.set_Nz(2);
    ic.set_X0(8.0);
    ic.set_radius(4.0);
    ic.set_amplitude(0.4);
    ic.set_rho(-0.036);
    ic.set_rseed(42);

    std::vector<double> &psi = aluminum.get_real_field("psi");
    std::fill(psi.begin(), psi.end(), -0.0060);
    ic.apply(aluminum, 0.0);

When the code was extended to handle multiple fields, get_real_field(const string &) and get_complex_field(const string &) was introduced. But we don't have any clever way to define where we actually apply initial condition to. Perhaps an easy solution would be to add void apply(Model &, const string &, double time) to FieldModifier. This same problem is also with boundary conditions.

Access boundary conditions from model

We need to create a method to retrieve boundary condition data from the model. The challenge is that boundary conditions are defined in the simulator, and the model does not have direct access to them, so we need to employ a workaround.

One way to do this might be

#include <iostream>

template <class Model> class Simulator {
public:
  void say_hi() { std::cout << "Hi" << std::endl; }
  void step() {
    Model model;
    post_hook(*this, model);
  }
};

class Model {};
class Tungsten : public Model {};
class Aluminum : public Model {};

// default post hook is no-op and is called for all models which does not have a
// post hook defined
template <class Model> void post_hook(Simulator<Model> &simulator, Model &model) {
  std::cout << "Default post hook" << std::endl;
}

void post_hook(Simulator<Tungsten> &simulator, Tungsten &model) {
  std::cout << "Tungsten post hook" << std::endl;
  // Tungsten ask simulator to say hi
  simulator.say_hi();
}

int main() {
  Simulator<Model> simulator1;
  simulator1.step();

  Simulator<Tungsten> simulator2;
  simulator2.step();

  Simulator<Aluminum> simulator3;
  simulator3.step();
}

Input file format

We should include descriptions of parameters to input file. The problem is that json does not support comments. In principle, they could be added but then all json validators will fail and comments must be stripped out before parsing the file. One possible option would be to add more structure, i.e. instead of having e.g.

{
    "name": "tungsten",
    "params": {
        "n0": -0.10,
        "alpha": 0.50,
        "n_sol": -0.047,
    }
}

we could have

{
    "name": "tungsten",
    "params": [
        {
            "name": "n0",
            "description": "Density of metastable fluid",
            "type": "number",
            "value": -0.10
        },
        {
            "name": "alpha",
            "description": "Thermal expansion coefficient",
            "type": "number",
            "value": 0.50
        },
        {
            "name": "n_sol",
            "description": "Density of solid",
            "type": "number",
            "value": -0.047
        }
    ]
}

Even better, we could introduce schema, and in that case, we could also validate (https://github.com/pboettch/json-schema-validator) the input against the schema and give clear error message if there is something wrong with the json file. Example:

{
    "schema": {
        "$schema": "http://json-schema.org/draft-07/schema#",
        "title": "Schema for Tungsten model",
        "properties": {
            "n0": {
                "description": "Density of metastable fluid",
                "type": "number"
            },
            "alpha": {
                "description": "Thermal expansion coefficient",
                "type": "number"
            },
            "n_sol": {
                "description": "Density of solid",
                "type": "number"
            }
        },
        "required": [
            "n0",
            "alpha",
            "n_sol"
        ],
        "type": "object"
    },

    "data": {
        "n0": -0.10,
        "alpha": 0.50,
        "n_sol": -0.047
    }
}

Of course, simulation data and schema can be on different files if that's better. I bet there's json editors that takes schema and builds nice user interface based on that so that json files can be easily done.

Add option to add initial and boundary condition to fields with different names

At the moment, support for multiple fields works only partially. They can be defined in model, but it's not possible to define any initial or boundary condition to other fields than default. Some modifications is needed, probably backward incompatible ones.

Description

Currently, we define initial and boundary conditions to fields in json file in this way:

    "initial_conditions": [
        {
            "type": "constant",
            "n0": -0.10
        },
        {
            "type": "seed_grid",
            "X0": 30.0,
            "Ny": 2,
            "Nz": 2,
            "radius": 25.0,
            "amplitude": 0.215936,
            "rho": -0.047
        }
    ],
    "boundary_conditions": [
        {
            "type": "moving",
            "rho_low": -0.464,
            "rho_high": -0.10,
            "width": 15.0,
            "alpha": 1.0,
            "disp": 40.0,
            "xpos": 127.17225402106772
        }
    ]

Like can seen, there is no any way to give option to which field these should be affected. Historically, we started with a field phi which was given by get_field, and later on it was extended that we can get any field by giving it's name, like get_field("myfield"). To preserve backward compatibility, we default to a field called default. For example, here we get the field inside boundary condition:

    const Decomposition &decomp = m.get_decomposition();
    Field &field = m.get_field();  // <-- defaults to m.get_field("default") !!
    const World &w = m.get_world();
    Vec3<int> low = decomp.inbox.low;
    Vec3<int> high = decomp.inbox.high;

There is no information on boundary condition / initial condition (a.k.a. "field modifier") to which field it should operate, and this needs to be changed. Perhaps introduce std::string m_field_name, some getter const std::string &get_field_name() const and things like that. So we can still add default field name default to member variable in order to maintain backward compatibility. Those need to be implemented to FieldModifier.

Then, in json file, we might have something like target or field_name which determines what field to operate on, and if not given, default:

        {
            "name": "phi",
            "type": "moving",
            "rho_low": -0.464,
            "rho_high": -0.10,
            "width": 15.0,
            "alpha": 1.0,
            "disp": 40.0,
            "xpos": 127.17225402106772
        }

Support GPU devices

HeFFTe is already supporting GPU devices, so it should be quite straightforward to start calculating using GPU.

Changing reshape algorithm from json does not work

    "fft_options": {
        "reshape_algorithm": "p2p_plined",
        "use_gpu_aware": true,
        "use_pencils": true,
        "use_reorder": true
    },
World: (Lx = 1024, Ly = 1024, Lz = 1024, x0 = -568.689, y0 = -568.689, z0 = -568.689, x1 = 568.689, y1 = 568.689, z0 = 568.689, dx = 1.111, dy = 1.111, dz = 1.111)
backend options: options = (fft1d:contiguous, mpi:alltoallv, decomposition:pencil, mpi:from-gpu)

In backend options, there clearly should be p2p_plined, not alltoallv.

Implement possibility to choose lattice type

We need to have an easy way to choose lattice type, at least BCC/FCC. The change in initial condition is something like

BCC:

    double s = 1.0 / sqrt(2.0);
    std::array<double, 3> q1 = {s, s, 0};
    std::array<double, 3> q2 = {s, 0, s};
    std::array<double, 3> q3 = {0, s, s};
    std::array<double, 3> q4 = {s, 0, -s};
    std::array<double, 3> q5 = {s, -s, 0};
    std::array<double, 3> q6 = {0, s, -s};
    std::array<std::array<double, 3>, 6> q = {q1, q2, q3, q4, q5, q6};

FCC:

    double s = 1.0 / sqrt(3.0);
    std::array<double, 3> q1 = {-s, s, s};
    std::array<double, 3> q2 = {s, -s, s};
    std::array<double, 3> q3 = {s, s, -s};
    std::array<double, 3> q4 = {s, s, s};
    std::array<std::array<double, 3>, 4> q = {q1, q2, q3, q4};

Among that, the operator is having two Gaussian peaks, which makes this a bit cumbersome to implement. Because lattice type is not only the matter of initial condition.

Change the file name for COPYING to CONTRIBUTING

The file for copyright transfer notification is named as COPYING. GitHub interprets the file to contain a license and lists it in the About sections as: "AGPL-3.0, Unknown licenses found". The AGPL license in interpreted based on the LICENSE file. The file name for the COPYING should be changed to e.g. CONTRIBUTING.

Fix nlohmann_json bug

include/openpfc/ui.hpp:23:10: fatal error: nlohmann/json.hpp: No such file or directory

Create binary openpfc

We should create a binary file openpfc. Later on, we can add some functionality to it, like openpfc compile mymodel.cpp or similar, but for now, let's at least have the file and with a command openpfc --version we output the current version of the code.

Fix license issues

The LICENSE file should be https://www.gnu.org/licenses/agpl-3.0.txt.

Every source code file should have the license notice in the beginning, e.g. (adjust the first line):

OpenPFC, a simulation software for the phase field crystal method.
Copyright (C) 2023 VTT Technical Research Centre of Finland Ltd.

This program is free software: you can redistribute it and/or modify
it under the terms of the GNU Affero General Public License as
published by the Free Software Foundation, either version 3 of the
License, or (at your option) any later version.

This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
GNU Affero General Public License for more details.

You should have received a copy of the GNU Affero General Public License
along with this program.  If not, see https://www.gnu.org/licenses/.

Installing OpenPFC does not install everything

Installing OpenPFC (cmake --install build) does not install the examples and apps to the installation directory (CMAKE_INSTALL_PREFIX), but they are in the build directory (in the source tree).

Error while loading shared libraries

error while loading shared libraries: libheffte.so.2: cannot open shared object file: No such file or directory

Cmake needs to set rpath correctly, perhaps...?

Fix warnings

[ 66%] Building CXX object tests/CMakeFiles/OpenPFCTests.dir/test_arraynd.cpp.o
In file included from /users/juaho/dev/openpfc/tests/test_arraynd.cpp:24:
In file included from /users/juaho/dev/openpfc/include/openpfc/array.hpp:27:
/users/juaho/dev/openpfc/include/openpfc/utils/show.hpp:52:97: warning: unused parameter 'offsets' [-Wunused-parameter]
void show(const std::vector<T> &data, const std::array<int, 2> &size, const std::array<int, 2> &offsets) {
                                                                                                ^
1 warning generated.
[ 67%] Building CXX object tests/CMakeFiles/OpenPFCTests.dir/test_world.cpp.o
[ 68%] Building CXX object tests/CMakeFiles/OpenPFCTests.dir/test_decomposition.cpp.o
[ 68%] Building CXX object tests/CMakeFiles/OpenPFCTests.dir/test_discrete_field.cpp.o
In file included from /users/juaho/dev/openpfc/tests/test_discrete_field.cpp:24:
In file included from /users/juaho/dev/openpfc/include/openpfc/discrete_field.hpp:24:
In file included from /users/juaho/dev/openpfc/include/openpfc/array.hpp:27:
/users/juaho/dev/openpfc/include/openpfc/utils/show.hpp:52:97: warning: unused parameter 'offsets' [-Wunused-parameter]
void show(const std::vector<T> &data, const std::array<int, 2> &size, const std::array<int, 2> &offsets) {
                                                                                                ^
1 warning generated.
[ 69%] Building CXX object tests/CMakeFiles/OpenPFCTests.dir/test_field_modifier.cpp.o
[ 69%] Building CXX object tests/CMakeFiles/OpenPFCTests.dir/test_fft.cpp.o
[ 70%] Building CXX object tests/CMakeFiles/OpenPFCTests.dir/test_ic_constant.cpp.o
[ 71%] Building CXX object tests/CMakeFiles/OpenPFCTests.dir/test_model.cpp.o
[ 71%] Building CXX object tests/CMakeFiles/OpenPFCTests.dir/test_multi_index.cpp.o
[ 72%] Building CXX object tests/CMakeFiles/OpenPFCTests.dir/test_simulator.cpp.o
[ 73%] Building CXX object tests/CMakeFiles/OpenPFCTests.dir/test_time.cpp.o
[ 73%] Linking CXX executable OpenPFCTests
[ 73%] Built target OpenPFCTests
[ 74%] Building CXX object apps/CMakeFiles/tungsten.dir/tungsten.cpp.o
In file included from /users/juaho/dev/openpfc/apps/tungsten.cpp:21:
In file included from /users/juaho/dev/openpfc/include/openpfc/openpfc.hpp:24:
In file included from /users/juaho/dev/openpfc/include/openpfc/array.hpp:27:
/users/juaho/dev/openpfc/include/openpfc/utils/show.hpp:52:97: warning: unused parameter 'offsets' [-Wunused-parameter]
void show(const std::vector<T> &data, const std::array<int, 2> &size, const std::array<int, 2> &offsets) {
                                                                                                ^
1 warning generated.
[ 74%] Linking CXX executable tungsten
[ 74%] Built target tungsten
[ 75%] Building CXX object apps/aluminumNew/CMakeFiles/aluminumTest.dir/aluminumTest.cpp.o
In file included from /users/juaho/dev/openpfc/apps/aluminumNew/aluminumTest.cpp:21:
In file included from /users/juaho/dev/openpfc/apps/aluminumNew/Aluminum.hpp:24:
In file included from /users/juaho/dev/openpfc/apps/aluminumNew/SeedGridFCC.hpp:24:
/users/juaho/dev/openpfc/apps/aluminumNew/SeedFCC.hpp:68:15: warning: suggest braces around initialization of subobject [-Wmissing-braces]
    mat3 C = {0};
              ^
              {}
In file included from /users/juaho/dev/openpfc/apps/aluminumNew/aluminumTest.cpp:21:
In file included from /users/juaho/dev/openpfc/apps/aluminumNew/Aluminum.hpp:24:
In file included from /users/juaho/dev/openpfc/apps/aluminumNew/SeedGridFCC.hpp:27:
In file included from /users/juaho/dev/openpfc/include/openpfc/openpfc.hpp:24:
In file included from /users/juaho/dev/openpfc/include/openpfc/array.hpp:27:
/users/juaho/dev/openpfc/include/openpfc/utils/show.hpp:52:97: warning: unused parameter 'offsets' [-Wunused-parameter]
void show(const std::vector<T> &data, const std::array<int, 2> &size, const std::array<int, 2> &offsets) {
                                                                                                ^
In file included from /users/juaho/dev/openpfc/apps/aluminumNew/aluminumTest.cpp:21:
In file included from /users/juaho/dev/openpfc/apps/aluminumNew/Aluminum.hpp:24:
In file included from /users/juaho/dev/openpfc/apps/aluminumNew/SeedGridFCC.hpp:24:
/users/juaho/dev/openpfc/apps/aluminumNew/SeedFCC.hpp:42:14: warning: private field 'orientation_' is not used [-Wunused-private-field]
  const vec3 orientation_;
             ^
In file included from /users/juaho/dev/openpfc/apps/aluminumNew/aluminumTest.cpp:21:
/users/juaho/dev/openpfc/apps/aluminumNew/Aluminum.hpp:41:8: warning: private field 'm_first' is not used [-Wunused-private-field]
  bool m_first = true;
       ^
4 warnings generated.
[ 76%] Linking CXX executable aluminumTest
[ 76%] Built target aluminumTest
[ 77%] Building CXX object apps/aluminumNew/CMakeFiles/aluminumNew.dir/aluminumNew.cpp.o
In file included from /users/juaho/dev/openpfc/apps/aluminumNew/aluminumNew.cpp:21:
In file included from /users/juaho/dev/openpfc/apps/aluminumNew/Aluminum.hpp:24:
In file included from /users/juaho/dev/openpfc/apps/aluminumNew/SeedGridFCC.hpp:24:
/users/juaho/dev/openpfc/apps/aluminumNew/SeedFCC.hpp:68:15: warning: suggest braces around initialization of subobject [-Wmissing-braces]
    mat3 C = {0};
              ^
              {}
In file included from /users/juaho/dev/openpfc/apps/aluminumNew/aluminumNew.cpp:21:
In file included from /users/juaho/dev/openpfc/apps/aluminumNew/Aluminum.hpp:24:
In file included from /users/juaho/dev/openpfc/apps/aluminumNew/SeedGridFCC.hpp:27:
In file included from /users/juaho/dev/openpfc/include/openpfc/openpfc.hpp:24:
In file included from /users/juaho/dev/openpfc/include/openpfc/array.hpp:27:
/users/juaho/dev/openpfc/include/openpfc/utils/show.hpp:52:97: warning: unused parameter 'offsets' [-Wunused-parameter]
void show(const std::vector<T> &data, const std::array<int, 2> &size, const std::array<int, 2> &offsets) {
                                                                                                ^
In file included from /users/juaho/dev/openpfc/apps/aluminumNew/aluminumNew.cpp:21:
In file included from /users/juaho/dev/openpfc/apps/aluminumNew/Aluminum.hpp:24:
In file included from /users/juaho/dev/openpfc/apps/aluminumNew/SeedGridFCC.hpp:24:
/users/juaho/dev/openpfc/apps/aluminumNew/SeedFCC.hpp:42:14: warning: private field 'orientation_' is not used [-Wunused-private-field]
  const vec3 orientation_;
             ^
In file included from /users/juaho/dev/openpfc/apps/aluminumNew/aluminumNew.cpp:21:
/users/juaho/dev/openpfc/apps/aluminumNew/Aluminum.hpp:41:8: warning: private field 'm_first' is not used [-Wunused-private-field]
  bool m_first = true;
       ^
4 warnings generated.
[ 77%] Linking CXX executable aluminumNew
[ 77%] Built target aluminumNew
[ 78%] Building CXX object examples/CMakeFiles/write_results.dir/write_results.cpp.o
[ 79%] Linking CXX executable write_results
[ 79%] Built target write_results
[ 80%] Building CXX object examples/CMakeFiles/mpi_timers.dir/mpi_timers.cpp.o
[ 80%] Linking CXX executable mpi_timers
[ 80%] Built target mpi_timers
[ 80%] Building CXX object examples/CMakeFiles/mpi_worker_inside_class.dir/mpi_worker_inside_class.cpp.o
[ 81%] Linking CXX executable mpi_worker_inside_class
[ 81%] Built target mpi_worker_inside_class
[ 82%] Building CXX object examples/CMakeFiles/08_discrete_fields.dir/08_discrete_fields.cpp.o
In file included from /users/juaho/dev/openpfc/examples/08_discrete_fields.cpp:23:
In file included from /users/juaho/dev/openpfc/include/openpfc/discrete_field.hpp:24:
In file included from /users/juaho/dev/openpfc/include/openpfc/array.hpp:27:
/users/juaho/dev/openpfc/include/openpfc/utils/show.hpp:52:97: warning: unused parameter 'offsets' [-Wunused-parameter]
void show(const std::vector<T> &data, const std::array<int, 2> &size, const std::array<int, 2> &offsets) {
                                                                                                ^
1 warning generated.
[ 82%] Linking CXX executable 08_discrete_fields
[ 82%] Built target 08_discrete_fields
[ 83%] Building CXX object examples/CMakeFiles/09_parallel_fft_high_level.dir/09_parallel_fft_high_level.cpp.o
In file included from /users/juaho/dev/openpfc/examples/09_parallel_fft_high_level.cpp:21:
In file included from /users/juaho/dev/openpfc/include/openpfc/openpfc.hpp:24:
In file included from /users/juaho/dev/openpfc/include/openpfc/array.hpp:27:
/users/juaho/dev/openpfc/include/openpfc/utils/show.hpp:52:97: warning: unused parameter 'offsets' [-Wunused-parameter]
void show(const std::vector<T> &data, const std::array<int, 2> &size, const std::array<int, 2> &offsets) {
                                                                                                ^
1 warning generated.
[ 84%] Linking CXX executable 09_parallel_fft_high_level
[ 84%] Built target 09_parallel_fft_high_level
[ 84%] Building CXX object examples/CMakeFiles/11_write_results.dir/11_write_results.cpp.o
In file included from /users/juaho/dev/openpfc/examples/11_write_results.cpp:24:
In file included from /users/juaho/dev/openpfc/include/openpfc/openpfc.hpp:24:
In file included from /users/juaho/dev/openpfc/include/openpfc/array.hpp:27:
/users/juaho/dev/openpfc/include/openpfc/utils/show.hpp:52:97: warning: unused parameter 'offsets' [-Wunused-parameter]
void show(const std::vector<T> &data, const std::array<int, 2> &size, const std::array<int, 2> &offsets) {
                                                                                                ^
1 warning generated.
[ 85%] Linking CXX executable 11_write_results
[ 85%] Built target 11_write_results
[ 85%] Building CXX object examples/CMakeFiles/json_read.dir/json_read.cpp.o
[ 86%] Linking CXX executable json_read
[ 86%] Built target json_read
[ 86%] Building CXX object examples/CMakeFiles/diffusion_model_with_custom_initial_condition.dir/diffusion_model_with_custom_initial_condition.cpp.o
In file included from /users/juaho/dev/openpfc/examples/diffusion_model_with_custom_initial_condition.cpp:21:
/users/juaho/dev/openpfc/examples/diffusion_model.hpp:34:14: warning: private field 'verbose' is not used [-Wunused-private-field]
  const bool verbose = false;
             ^
1 warning generated.
[ 87%] Linking CXX executable diffusion_model_with_custom_initial_condition
[ 87%] Built target diffusion_model_with_custom_initial_condition
[ 88%] Building CXX object examples/CMakeFiles/mpi_worker.dir/mpi_worker.cpp.o
[ 89%] Linking CXX executable mpi_worker
[ 89%] Built target mpi_worker
[ 89%] Building CXX object examples/CMakeFiles/02_domain_decomposition.dir/02_domain_decomposition.cpp.o
[ 90%] Linking CXX executable 02_domain_decomposition
[ 90%] Built target 02_domain_decomposition
[ 91%] Building CXX object examples/CMakeFiles/03_parallel_fft.dir/03_parallel_fft.cpp.o
[ 91%] Linking CXX executable 03_parallel_fft
[ 91%] Built target 03_parallel_fft
[ 91%] Building CXX object examples/CMakeFiles/06_multi_index.dir/06_multi_index.cpp.o
[ 92%] Linking CXX executable 06_multi_index
[ 92%] Built target 06_multi_index
[ 93%] Building CXX object examples/CMakeFiles/04_diffusion_model.dir/04_diffusion_model.cpp.o
[ 93%] Linking CXX executable 04_diffusion_model
[ 93%] Built target 04_diffusion_model
[ 94%] Building CXX object examples/CMakeFiles/05_simulator.dir/05_simulator.cpp.o
[ 95%] Linking CXX executable 05_simulator
[ 95%] Built target 05_simulator
[ 96%] Building CXX object examples/CMakeFiles/07_array.dir/07_array.cpp.o
In file included from /users/juaho/dev/openpfc/examples/07_array.cpp:23:
In file included from /users/juaho/dev/openpfc/include/openpfc/array.hpp:27:
/users/juaho/dev/openpfc/include/openpfc/utils/show.hpp:52:97: warning: unused parameter 'offsets' [-Wunused-parameter]
void show(const std::vector<T> &data, const std::array<int, 2> &size, const std::array<int, 2> &offsets) {
                                                                                                ^
1 warning generated.
[ 96%] Linking CXX executable 07_array
[ 96%] Built target 07_array
[ 97%] Building CXX object examples/CMakeFiles/12_cahn_hilliard.dir/12_cahn_hilliard.cpp.o
In file included from /users/juaho/dev/openpfc/examples/12_cahn_hilliard.cpp:23:
In file included from /users/juaho/dev/openpfc/include/openpfc/openpfc.hpp:24:
In file included from /users/juaho/dev/openpfc/include/openpfc/array.hpp:27:
/users/juaho/dev/openpfc/include/openpfc/utils/show.hpp:52:97: warning: unused parameter 'offsets' [-Wunused-parameter]
void show(const std::vector<T> &data, const std::array<int, 2> &size, const std::array<int, 2> &offsets) {
                                                                                                ^
/users/juaho/dev/openpfc/examples/12_cahn_hilliard.cpp:29:14: warning: unused variable 'PI' [-Wunused-const-variable]
const double PI = 3.141592653589793238463;
             ^
2 warnings generated.
[ 97%] Linking CXX executable 12_cahn_hilliard
[ 97%] Built target 12_cahn_hilliard
[ 97%] Building CXX object examples/CMakeFiles/time.dir/time.cpp.o
[ 98%] Linking CXX executable time
[ 98%] Built target time
[ 98%] Building CXX object examples/CMakeFiles/01_hello_world.dir/01_hello_world.cpp.o
[ 99%] Linking CXX executable 01_hello_world
[ 99%] Built target 01_hello_world
[ 99%] Building CXX object examples/CMakeFiles/10_ui_register_ic.dir/10_ui_register_ic.cpp.o
In file included from /users/juaho/dev/openpfc/examples/10_ui_register_ic.cpp:22:
In file included from /users/juaho/dev/openpfc/include/openpfc/openpfc.hpp:24:
In file included from /users/juaho/dev/openpfc/include/openpfc/array.hpp:27:
/users/juaho/dev/openpfc/include/openpfc/utils/show.hpp:52:97: warning: unused parameter 'offsets' [-Wunused-parameter]
void show(const std::vector<T> &data, const std::array<int, 2> &size, const std::array<int, 2> &offsets) {
                                                                                                ^
1 warning generated.
[100%] Linking CXX executable 10_ui_register_ic
[100%] Built target 10_ui_register_ic

Fix failing CICD

The reason is lcov. Maybe we need to disable it, at least for temporary. It's not working that good anyway.

Implement callback functionality

User should be able to define own callback function which is then called after each time step. This way user can e.g. query some extra information from the simulation or modify the simulation on the fly.

Fix warning about unused variable xpos

There istwo warnings about an unused variable "xpos":

...
In file included from /home/jussi/OpenPFC/OpenPFC-0.1.0-source/apps/aluminumNew/aluminumTest.cpp:1:
/home/jussi/OpenPFC/OpenPFC-0.1.0-source/apps/aluminumNew/Aluminum.hpp: In member function ‘virtual void Aluminum::step(double)’:
/home/jussi/OpenPFC/OpenPFC-0.1.0-source/apps/aluminumNew/Aluminum.hpp:274:12: warning: unused variable ‘xpos’ [-Wunused-variable]
274 |     double xpos = fmod(params.m_xpos, l);
|            ^~~~
[ 92%] Linking CXX executable 02_domain_decomposition
In file included from /home/jussi/OpenPFC/OpenPFC-0.1.0-source/apps/aluminumNew/aluminumNew.cpp:1:
/home/jussi/OpenPFC/OpenPFC-0.1.0-source/apps/aluminumNew/Aluminum.hpp: In member function ‘virtual void Aluminum::step(double)’:
/home/jussi/OpenPFC/OpenPFC-0.1.0-source/apps/aluminumNew/Aluminum.hpp:274:12: warning: unused variable ‘xpos’ [-Wunused-variable]
274 |     double xpos = fmod(params.m_xpos, l);
|            ^~~~
...

Moving boundary condition interface tracking improvement

Currently for the implementation of the moving boundary condition the interface is tracked by taking the first/last point where the value of the field of interest passes a hard-set value of 0.1. This works quite well for the phi field for a solid, since the atomic peaks are very likely to cross that value, but might not work for phi with a very low amplitude or low average density, and will not work for a continuously changing field such as concentration.
Instead, this boundary condition should probably track the local average of the field of interest (such as how the mean-field phi is implemented in the main code) and see where that crosses a threshold value set by the user - by default, the midpoint between the high and low densities.
Alternatively, for the phi field at least, it is possible to track the interface location by computing the amplitude of the field at a point A[x] = |n|_mf - nmf, and this will be zero in the liquid and nonzero in the solid. That wouldn't work for concentrations, though.

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.