From 3629b544de8e20314e8306efafc9efb89c0238a1 Mon Sep 17 00:00:00 2001 From: Gary Hu Date: Wed, 24 Apr 2024 16:05:11 -0500 Subject: [PATCH 01/55] Add a syntax executable --- doc/CMakeLists.txt | 9 ++++++--- .../base/test_Registry.cxx => doc/syntax.cxx | 18 ++++++++---------- 2 files changed, 14 insertions(+), 13 deletions(-) rename tests/unit/base/test_Registry.cxx => doc/syntax.cxx (88%) diff --git a/doc/CMakeLists.txt b/doc/CMakeLists.txt index f62bd4539a..c2b58e1757 100644 --- a/doc/CMakeLists.txt +++ b/doc/CMakeLists.txt @@ -38,11 +38,14 @@ configure_file( ${NEML2_BINARY_DIR}/doc/DoxyfileLATEX.sh ) +# Build a dummy program to extract all the syntax +add_executable(syntax syntax.cxx) +target_link_libraries(syntax PRIVATE neml2) add_custom_target(doc-syntax - DEPENDS unit_tests + DEPENDS syntax WORKING_DIRECTORY ${NEML2_BINARY_DIR}/doc - COMMAND ${NEML2_BINARY_DIR}/tests/unit_tests Registry -c syntax -r compact - COMMAND ${Python_EXECUTABLE} ${NEML2_SOURCE_DIR}/scripts/syntax_to_md.py syntax.yml content/_99_syntax.md + COMMAND ${NEML2_BINARY_DIR}/doc/syntax + COMMAND ${Python_EXECUTABLE} ${NEML2_SOURCE_DIR}/scripts/syntax_to_md.py syntax.txt content/_99_syntax.md COMMENT "Generate NEML2 syntax and convert to markdown" VERBATIM ) diff --git a/tests/unit/base/test_Registry.cxx b/doc/syntax.cxx similarity index 88% rename from tests/unit/base/test_Registry.cxx rename to doc/syntax.cxx index 866d85d290..45f23d9564 100644 --- a/tests/unit/base/test_Registry.cxx +++ b/doc/syntax.cxx @@ -21,18 +21,16 @@ // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN // THE SOFTWARE. -#include -#include #include "neml2/base/Registry.h" +#include -using namespace neml2; - -TEST_CASE("Registry", "[base]") +int +main() { - SECTION("syntax") - { - std::ofstream out("syntax.yml"); - Registry::print(out); - } + std::ofstream syntax_file; + syntax_file.open("syntax.txt"); + neml2::Registry::print(syntax_file); + syntax_file.close(); + return 0; } From eb927aa5cc40599b0b1df9b5e1c70e514ad86833 Mon Sep 17 00:00:00 2001 From: Gary Hu Date: Thu, 2 May 2024 09:44:18 -0500 Subject: [PATCH 02/55] Update doxygen and theme version; Remove the doxygen-awesome-css submodule --- .gitmodules | 3 --- doc/CMakeLists.txt | 11 ++++++++++- doc/config/HTML.in | 4 ++-- extern/doxygen-awesome-css | 1 - 4 files changed, 12 insertions(+), 7 deletions(-) delete mode 160000 extern/doxygen-awesome-css diff --git a/.gitmodules b/.gitmodules index 614a0dc2d8..9487632756 100644 --- a/.gitmodules +++ b/.gitmodules @@ -2,9 +2,6 @@ path = extern/Catch2 url = https://github.com/catchorg/Catch2.git ignore = dirty -[submodule "extern/doxygen-awesome-css"] - path = extern/doxygen-awesome-css - url = https://github.com/jothepro/doxygen-awesome-css.git [submodule "extern/hit/hit"] path = extern/hit/hit url = https://github.com/idaholab/hit.git diff --git a/doc/CMakeLists.txt b/doc/CMakeLists.txt index c2b58e1757..4467ddae62 100644 --- a/doc/CMakeLists.txt +++ b/doc/CMakeLists.txt @@ -2,7 +2,7 @@ if(NOT DEFINED DOXYGEN_EXECUTABLE) if(UNIX AND NOT APPLE) FetchContent_Declare( doxygen - URL https://github.com/doxygen/doxygen/releases/download/Release_1_9_8/doxygen-1.9.8.linux.bin.tar.gz + URL https://github.com/doxygen/doxygen/releases/download/Release_1_10_0/doxygen-1.10.0.linux.bin.tar.gz ) message(STATUS "Downloading doxygen, this may take a few minutes.") FetchContent_MakeAvailable(doxygen) @@ -12,6 +12,15 @@ if(NOT DEFINED DOXYGEN_EXECUTABLE) endif() endif() +# Theme for the doxygen documentation +FetchContent_Declare( + doxygen-awesome-css + GIT_REPOSITORY https://github.com/jothepro/doxygen-awesome-css.git + GIT_TAG 5b27b3a747ca1e559fa54149762cca0bad6036fb # version 2.3.2 +) +message(STATUS "Downloading doxygen-awesome-css, this may take a few minutes.") +FetchContent_MakeAvailable(doxygen-awesome-css) + find_package(Python COMPONENTS Interpreter) # Generate Doxyfile for HTML diff --git a/doc/config/HTML.in b/doc/config/HTML.in index d9b7abfcb9..1975020cc7 100644 --- a/doc/config/HTML.in +++ b/doc/config/HTML.in @@ -3,8 +3,8 @@ #--------------------------------------------------------------------------- GENERATE_HTML = YES HTML_OUTPUT = html -HTML_EXTRA_STYLESHEET = ${NEML2_SOURCE_DIR}/extern/doxygen-awesome-css/doxygen-awesome.css \ - ${NEML2_SOURCE_DIR}/extern/doxygen-awesome-css/doxygen-awesome-sidebar-only.css +HTML_EXTRA_STYLESHEET = ${doxygen-awesome-css_SOURCE_DIR}/doxygen-awesome.css \ + ${doxygen-awesome-css_SOURCE_DIR}/doxygen-awesome-sidebar-only.css GENERATE_TREEVIEW = YES USE_MATHJAX = YES MATHJAX_VERSION = MathJax_3 diff --git a/extern/doxygen-awesome-css b/extern/doxygen-awesome-css deleted file mode 160000 index a3c119b479..0000000000 --- a/extern/doxygen-awesome-css +++ /dev/null @@ -1 +0,0 @@ -Subproject commit a3c119b4797be2039761ec1fa0731f038e3026f6 From 9b58d05d6c320c7c1512d23f31af4f96c6a22c4b Mon Sep 17 00:00:00 2001 From: Gary Hu Date: Thu, 2 May 2024 15:19:45 -0500 Subject: [PATCH 03/55] Reorganize doc content; Update developer guide according to the current release --- README.md | 33 +-- doc/config/DoxygenLayout.xml | 11 +- doc/config/HTML.in | 5 +- doc/content/_01_install.md | 181 +++++------- doc/content/_02_math.md | 79 ------ doc/content/_02_user.md | 3 + doc/content/_03_dev.md | 533 +++++++++++++++++++++++++++++++++++ doc/content/_03_impl.md | 270 ------------------ doc/content/_04_devel.md | 278 ------------------ doc/content/_04_system.md | 3 + src/neml2/CMakeLists.txt | 6 +- 11 files changed, 630 insertions(+), 772 deletions(-) delete mode 100644 doc/content/_02_math.md create mode 100644 doc/content/_02_user.md create mode 100644 doc/content/_03_dev.md delete mode 100644 doc/content/_03_impl.md delete mode 100644 doc/content/_04_devel.md create mode 100644 doc/content/_04_system.md diff --git a/README.md b/README.md index 50f4964565..ea170f549b 100644 --- a/README.md +++ b/README.md @@ -4,30 +4,27 @@ ### The New Engineering Material model Library, version 2 -NEML2 is an offshoot of [NEML](https://github.com/Argonne-National-Laboratory/neml), an earlier constitutive modeling code developed at Argonne National Laboratory. -Like NEML, NEML2 provides a flexible, modular way to build constitutive models from smaller blocks. -Unlike NEML, NEML2 vectorizes the constitutive update to efficiently run on GPUs. NEML2 is built on top of [PyTorch](https://pytorch.org/cppdocs/) -to provide GPU support, but this also means that NEML2 models have all the features of a PyTorch module. So, for example, users can take derivatives of the model with respect to parameters using automatic differentiation. +NEML2 is an offshoot of [NEML](https://github.com/Argonne-National-Laboratory/neml), an earlier material modeling code developed at Argonne National Laboratory. +Like its predecessor, NEML2 provides a flexible, modular way to build material models from smaller blocks. +Unlike its predecessor, NEML2 vectorizes the material update to efficiently run on GPUs. NEML2 is built on top of [PyTorch](https://pytorch.org/cppdocs/) to provide GPU support, and this also means that NEML2 models have all the features of a PyTorch module (i.e. `torch.nn.module`). So, for example, users can take derivatives of the model with respect to parameters using automatic differentiation. NEML2 is provided as open source software under a MIT [license](https://raw.githubusercontent.com/reverendbedford/neml2/main/LICENSE). -### Build and installation +> **Disclaimer** +> +> NEML2 is _not_ a database of material models. There are many example material models in the library for testing and verification purposes. These models do not represent the response of any actual material. -Building should be as easy as cloning the repository, configuring with CMake, building with `make`, and testing with `make test`. -By default NEML2 will download the current CPU-only version of torch. To instead use a system torch set CMake options `-DLIBTORCH_DIR=/path/to/your/torch`. If you use the default build you will get a CPU-only version of torch and performance might suffer compared to a CUDA version. +### Installation -### NEML2 features and design philosophy +Building should be as easy as cloning the repository, configuring with CMake, building with `make`, testing with `make test`, and installing with `make install`. -- **Modular constitutive models**: NEML2 material models are modular – they are built up from smaller pieces into a complete model. For example, a model might piece together a temperature-dependent elasticity model, a yield surface, a flow rule, and several hardening rules. Each of these submodels is independent of the other objects so that, for example, switching from conventional J2 plasticity to a non J2 theory requires only a one line change in an input file, if the model is already implemented, or a relatively small amount of coding to add the new yield surface if it has not been implemented. All of these objects are interchangeable. For example, the damage, viscoplastic, and rate-independent plasticity models all use the same yield (flow) surfaces, hardening rules, elasticity models, and so on. -- **Extensible constitutive models**: The library is structured so that adding a new feature to an existing material model should be as simple as possible and require as little code as possible. As part of this philosophy, the library only requires new components provide a few partial derivatives and NEML uses this information to assemble the Jacobian needed to do a fully implement, backward Euler integration of the ordinary differential equations comprising the model form and to provide the algorithmic tangent needed to integrate the model into an implicit finite element framework. Moreover, in NEML2 implementations can forgo providing these partial derivatives and NEML2 will calculate them with automatic differentiation -- albeit at a significant performance cost. -- **Friendly user interfaces**: There are two general ways to create and interface with NEML2 material models: the python bindings and the compiled library with [HIT](https://github.com/idaholab/moose/tree/next/framework/contrib/hit) input. The python bindings are generally used for creating, fitting, and debugging new material models. In python, a material model is built up object-by-object and assembled into a complete mathematical constitutive relation. NEML2 provides several python drivers for exercising these material models in simple loading configurations. These drivers include common test types, like uniaxial tension tests and strain-controlled cyclic fatigue tests along with more esoteric drivers supporting simplified models of high temperature pressure vessels, like n-bar models and generalized plane-strain axisymmetry. NEML2 provides a full Abaqus UMAT interface and examples of how to link the compiled library into C, C++, or Fortran codes. These interfaces can be used to call NEML2 models from finite element codes. When using the compiled library, NEML2 models can be created and archived using a hierarchical HIT format. -- **Strict quality assurance**: NEML2 is developed under a strict quality assurance program. Because the NEML2 distribution does not provide full, parameterized models for any actual materials, ensuring the quality of the library is a verification problem – testing to make sure that NEML2 is correctly implementing the mathematical models – rather than a validation problem of comparing the results of a model to an actual test. This verification is done with extensive unit testing. This unit testing verifies every mathematical function and every derivative in the library is correctly implemented. -- **CPU/GPU Vectorization**: NEML2 models can be vectorized, meaning that a large batch of constitutive models can be evaluated simultaneously. The vectorized model can be evaluated both on CPU and on GPU, with a unified, intuitive user interface. -- **Flexible model composition**: NEML2 offers a more flexible way of composing models. Each individual model only defines the forward operator (and optionally its derivative) with a given set of inputs and outputs, without knowing anything a priori about how it is going to be used. When a set of models are *composed* together to form a composite model, dependencies among different models are automatically detected, registered, and resolved. The user has *complete control* over how NEML2 evaluates a set of models. -- **Faster evaluation of chained models**: As a result the dependency resolution mentioned above, an optimal order of evaluating the composed model is used to perform the forward operation -- every model in the dependency graph is evaluated once and only once, avoiding any redundant calculations. -- **General implicit update**: NEML2 offers a general interface for defining implicit models, unlike NEML which requires the implicit function to be in the form of an ODE. +By default, NEML2 will be compiled against the PyTorch package in the current environment. Finer control over various dependencies as well as other build customization is documented in the [Installation Guide](https://reverendbedford.github.io/neml2/install.html). -### Disclaimer +### Features and design philosophy -NEML2 does not provide a database of models for any particular class of materials. There are many example materials contained in the library release. These models are included entirely for illustrative purposes and do not represent the response of any actual material. Right now these models are solid mechanics constitutive models, providing the stress/strain response of materials. However, NEML2 is general enough to build models of any type. +- **Vectorization**: NEML2 models can be vectorized, meaning that a large _batch_ of material models can be evaluated simultaneously. The vectorized models can be evaluated on both CPU and GPU. Moreover, NEML2 provides a unified implementation and user interface, for both developers and end users, that work on all supported devices. +- **Modular and flexible material models**: NEML2 material models are modular – they are built up from smaller pieces into a complete model. NEML2 offers an extremely flexible way of composing models. Each individual model only defines the forward operator (and optionally its derivative) with a given set of inputs and outputs. When a set of models are *composed* together to form a composite model, dependencies among different models are automatically detected, registered, and resolved. The user has *complete control* over how NEML2 evaluates a set of models. +- **Extensible material models**: The library is structured so that adding a new feature to an existing material model should be as simple as possible and require as little code as possible. In line with this philosophy, the library only requires new components to provide a few partial derivatives, and NEML2 uses this information to automatically assemble the overall Jacobian using chain rule and provide the algorithmic tangent needed to integrate the model into an implicit finite element framework. Moreover, in NEML2, implementations can forgo providing these partial derivatives, and NEML2 will calculate them with automatic differentiation. +- **Friendly user interfaces**: There are two general ways of interfacing with NEML2 material models: the compiled C++ library and the Python bindings. In both interfaces, creation and archival of NEML2 models rely on input files written in the hierarchical [HIT](https://github.com/idaholab/moose/tree/master/framework/contrib/hit) format. NEML2 models created using the Pytbon bindings are fully compatible with PyTorch's automatic differentiation, meaning that NEML2 material models can seamlessly work with popular machine learning frameworks. +- **Strict quality assurance**: NEML2 is developed under a strict quality assurance program. Because the NEML2 distributions do not provide full, parameterized models for any actual materials, ensuring the quality of the library is a verification problem – testing to make sure that NEML2 is correctly implementing the mathematical models – rather than a validation problem of comparing the results of a model to an experiment. In NEML2, this verification is done with extensive unit testing. Additional regression tests are set up for each combination of material model to ensure result consistency across releases. diff --git a/doc/config/DoxygenLayout.xml b/doc/config/DoxygenLayout.xml index b6e2a4a808..b81a9b1e0d 100644 --- a/doc/config/DoxygenLayout.xml +++ b/doc/config/DoxygenLayout.xml @@ -1,12 +1,11 @@ - - - - - - + + + + + diff --git a/doc/config/HTML.in b/doc/config/HTML.in index 1975020cc7..eac7f0bb17 100644 --- a/doc/config/HTML.in +++ b/doc/config/HTML.in @@ -2,9 +2,12 @@ # Configuration options related to the HTML output #--------------------------------------------------------------------------- GENERATE_HTML = YES +DISABLE_INDEX = NO +FULL_SIDEBAR = NO HTML_OUTPUT = html -HTML_EXTRA_STYLESHEET = ${doxygen-awesome-css_SOURCE_DIR}/doxygen-awesome.css \ +HTML_EXTRA_STYLESHEET = ${doxygen-awesome-css_SOURCE_DIR}/doxygen-awesome.css\ ${doxygen-awesome-css_SOURCE_DIR}/doxygen-awesome-sidebar-only.css +HTML_COLORSTYLE = LIGHT GENERATE_TREEVIEW = YES USE_MATHJAX = YES MATHJAX_VERSION = MathJax_3 diff --git a/doc/content/_01_install.md b/doc/content/_01_install.md index 203ba39e9e..dadb398383 100644 --- a/doc/content/_01_install.md +++ b/doc/content/_01_install.md @@ -1,32 +1,44 @@ -# Getting Started {#install} +# Installation Guide {#install} [TOC] -## Choose the compute platform +## Prerequisites -NEML2 can be compiled to support two compute platforms: -- CPU -- CUDA - -Choose the CPU compute platform if you only need to run NEML2 using CPUs. Choose the CUDA compute platform if you plan to take advantage of the GPU(s) of the computer. +Compiling the NEML2 core library requires +- A C++ compiler with C++17 support +- CMake >= 3.23 ## Dependencies -NEML2 depends on the following drivers/packages/libraries: -| CPU | CUDA | Dependency | Version | -| :---: | :---: | :----------- | :------ | -| x | x | C++ compiler | >=17 | -| x | x | CMake | >=3.1 | -| x | x | libTorch | | -| | x | CUDA toolkit | | +### Required dependencies + +- [PyTorch](https://pytorch.org/get-started/locally/), version 2.2.2. + +Other PyTorch releases with a few minor versions around are likely to be compatible. In the PyTorch official download page, several download options are provided: conda, pip, libTorch, and source distribution. +- **Recommended** If you choose to download PyTorch using conda or pip, the NEML2 CMake script can automatically detect and use the PyTorch installation. +- If you choose to download libTorch or build PyTorch from source, you will need to set `LIBTORCH_DIR` to be the location of libTorch when using CMake to configure NEML2. +- If no PyTorch installation can be detected and `LIBTORCH_DIR` is not set at configure time, the NEML2 CMake script will automatically download and use the libTorch obtained from the official website. Note, however, that this method only works on Linux and Mac systems. + +> The libTorch distributions from the official website come with two flavors: "Pre-cxx11 ABI" and "cxx11 ABI". Both variants are supported by NEML2. If you are unsure, we recommend the one with "cxx11 ABI". -See [notes on obtaining the dependencies](@ref NotesOnObtainingTheDependencies) for some guidance. +### Optional dependencies -For developers, some additional dependencies are recommended (regardless of the compute platform): -- [Doxygen](https://www.doxygen.nl/), the documentation generator -- [clang-format](https://clang.llvm.org/docs/ClangFormat.html), the C++ code formatter +*No action is needed to manually obtain the optional dependencies.* The compatible optional dependencies will be automatically downloaded and configured by CMake depending on the build customization. -## Install NEML2 +- [HIT](https://github.com/idaholab/moose/tree/master/framework/contrib/hit) for input file parsing. +- [Catch2](https://github.com/catchorg/Catch2) for unit and regression testing. +- [gperftools](https://github.com/gperftools/gperftools) for profiling. +- [Doxygen](https://github.com/doxygen/doxygen) for building the documentation. +- [Doxygen Awesome](https://github.com/jothepro/doxygen-awesome-css) the documentation theme. +- Python packages + - PyYAML + - pandas + - matplotlib + - pybind11 + - pybind11-stubgen + - pytest + +## Build, Test, and Install First, obtain the NEML2 source code. @@ -34,119 +46,56 @@ First, obtain the NEML2 source code. git clone https://github.com/reverendbedford/neml2.git cd neml2 git checkout main -git submodule update --recursive --init ``` -Then, configure NEML2 and generate the Makefile. +Then, configure NEML2. See [build customization](#build-customization) for possible configuration options. ``` -cmake -DCMAKE_PREFIX_PATH=/path/to/torch/share/cmake . +mkdir build && cmake -B build . ``` -where `/path/to/torch/share/cmake` is the path to the `share/cmake` directory of your libTorch installation. Finally, compile NEML2. ``` -make -j N +cd build && make -j N ``` -where `N` is the number of processors to use for parallel compilation. +where `N` is the number of cores to use for parallel compilation. + +After the compilation is complete, optionally run the tests to make sure the compilation was successful. -After the compilation finishes, you can run the tests to make sure the build was successful: ``` make test ``` -## Notes on obtaining the dependencies {#NotesOnObtainingTheDependencies} - -Feel free to create a ticket at [https://github.com/reverendbedford/neml2/issues](https://github.com/reverendbedford/neml2/issues) if you run into issues about installing the dependencies. - -Typically, the C++ compiler, a reasonably modern CMake, Doxygen, and clang-format can be obtained via the system package manager. - -libTorch can be downloaded from the official PyTorch website: [https://pytorch.org/get-started/locally/](https://pytorch.org/get-started/locally/). Choose the compute platform in consistency with the NEML2 compute platform. Also take a note of the version of the libTorch-compatible CUDA if you chose CUDA as the compute platform. Note that there are two versions of libTorch: "Pre-cxx11 ABI" and "cxx11 ABI". Both versions are supported by NEML2. If you are unsure, we recommend the one with "cxx11 ABI". - -> In the future we may provide an option to automatically install a compatible version of libTorch. - -There are many ways of installing the NVIDIA driver and the CUDA toolkit. We will not try to make a recommendation here. However, do make sure the NVIDIA driver is compatible with your GPU. It is also recommended to install a CUDA toolkit with the same version number as the libTorch CUDA version. - -## Quick Start {#user} - -NEML2 uses the [HIT](https://github.com/idaholab/hit) format, a simple hierarchical text language, for model specification. More generally speaking, HIT is the canonical language used in NEML2 for serialization, deserialization, and archival purposes. - -The NEML2 input files have extension `.i`. An example input file is shown below -```python -[Tensors] - [end_time] - type = LogSpaceTensor - start = -1 - end = 5 - steps = 20 - [] - [times] - type = LinSpaceTensor - end = end_time - steps = 100 - [] - [max_strain] - type = InitializedSymR2 - values = '0.1 -0.05 -0.05' - nbatch = 20 - [] - [strains] - type = LinSpaceTensor - end = max_strain - steps = 100 - [] -[] - -[Drivers] - [driver] - type = SolidMechanicsDriver - model = 'model' - times = 'times' - prescribed_strains = 'strains' - save_as = 'result.pt' - [] - [regression] - type = TransientRegression - driver = 'driver' - reference = 'gold/result.pt' - [] -[] - -[Solvers] - [newton] - type = Newton - [] -[] - -[Models] - [implicit_rate] - type = ComposedModel - models = 'mandel_stress vonmises yield normality flow_rate Eprate Erate Eerate elasticity integrate_stress' - [] - [model] - type = ImplicitUpdate - implicit_model = 'implicit_rate' - solver = 'newton' - [] -[] -``` -There are four top-level sections: -- `[Tensors]`: Tensors specified from within the input file. -- `[Solvers]`: Solvers for solving an implicit model. -- `[Models]`: NEML2 constitutive models. -- `[Drivers]`: Drivers used to evaluate/test models. - -The user has full control over the sub-sections under the top-level sections, called _objects_. The sub-section name is used as the name of the object. Each object reserves a special field named "type". NEML2 parses the value in the "type" field and constructs the corresponding object at run time. The syntax and options for all objects are listed in the [syntax documentation](@ref syntax). - -Currently, NEML2 does not maintain a set of examples. However, the regression tests shall serve as decent input file templates. The regression tests are located in `/tests/regression` in the repository. +The compiled NEML2 can be installed as a system library. +``` +make install +``` -## Next steps +## Build Customization {#build-customization} -Depending on your specific use case, the following resources might be useful: +Additional configuration options can be passed via command line using the `-DOPTION` or `-DOPTION=ON` format. For example, -- [Mathematical conventions](@ref math) introduces the common mathematical notations used throughout this documentation. -- [Implementation](@ref impl) describes the basics of the library architecture and design philosophy. They can be a good starting point if you want to create your own material model within the NEML2 framework. -- [Model development](@ref devel) is a step-by-step guide for building a constitutive model. It uses the small deformation \f$J_2\f$ isotropic viscoplasticity as an example. -- [Class list](https://reverendbedford.github.io/neml2/annotated.html) is a complete list of class doxygen documentations. +``` +cmake -DNEML2_DOC=ON -B build . +``` +turns on the `NEML2_DOC` option, and additional targets for building the Doxygen documentation will be created inside the Makefile. Note that this would also download additional optional dependencies that are required to build the documentation. + +Commonly used configuration options are summarized below. Default options are underlined. + +| Option | Values (default) | Description | +| :------------------- | :---------------------------------------------------------- | :---------------------------------------------------------------------------------------- | +| CMAKE_BUILD_TYPE | Debug, Release, MinSizeRel, RelWithDebInfo, Coverage | CMake [Reference](https://cmake.org/cmake/help/latest/variable/CMAKE_BUILD_TYPE.html) | +| CMAKE_INSTALL_PREFIX | | CMake [Reference](https://cmake.org/cmake/help/latest/variable/CMAKE_INSTALL_PREFIX.html) | +| CMAKE_UNITY_BUILD | | CMake [Reference](https://cmake.org/cmake/help/latest/variable/CMAKE_UNITY_BUILD.html) | +| NEML2_DTYPE | Float16, Float32, Float64 | Default floating point integral type used in the material models | +| NEML2_INT_DTYPE | Int8, Int16, Int32, Int64 | Default fixed point integral type used in the material models | +| BUILD_TESTING | ON, OFF | Master knob for including/excluding all tests | +| NEML2_UNIT | ON, OFF | Create the unit testing target | +| NEML2_REGRESSION | ON, OFF | Create the regression testing target | +| NEML2_VERIFICATION | ON, OFF | Create the verification testing target | +| NEML2_BENCHMARK | ON, OFF | Create the benchmark testing target | +| NEML2_PROFILING | ON, OFF | Create the profiling executable target | +| NEML2_DOC | ON, OFF | Create the documentation target | +| NEML2_PYBIND | ON, OFF | Create the Python bindings target | diff --git a/doc/content/_02_math.md b/doc/content/_02_math.md deleted file mode 100644 index e3a6886f88..0000000000 --- a/doc/content/_02_math.md +++ /dev/null @@ -1,79 +0,0 @@ -# Mathematical Conventions {#math} - -[TOC] - -## Typography - -The manual uses a lower case, standard font \f$x\f$ to represent a scalar, a bold lower case to represent a vector -\f$\mathbf{x}\f$, a bold upper case to represent a second order tensor \f$\mathbf{x}\f$, and a bold upper case Fraktur to represent a fourth order tensor \f$\mathbf{\mathfrak{X}}\f$. - -There are certain exceptions to the upper case/lower case convention for commonly used notation. For example the stress and strain tensors are denoted \f$\mathbf{\sigma}\f$ and \f$\mathbf{\varepsilon}\f$, respectively. - -Internally in NEML2 all tensor operations are implemented as [Mandel](#mandel) dot products. However, the documentation writes the full tensor form of the equations. - -NEML2 models work in three dimensions. This means second order tensors can be expressed by 3-by-3 arrays and -fourth order tensors are 3-by-3-by-3-by-3 arrays. - -## Mandel notation {#mandel} - -NEML2 uses the Mandel notation to convert symmetric second and fourth order tensors to vectors and matrices. -The convention transforms the second order tensor -\f[ - \left[\begin{array}{ccc} - \sigma_{11} & \sigma_{12} & \sigma_{13}\\ - \sigma_{12} & \sigma_{22} & \sigma_{23}\\ - \sigma_{13} & \sigma_{23} & \sigma_{33} - \end{array}\right] -\f] -to -\f[ - \left[\begin{array}{cccccc} - \sigma_{11} & \sigma_{22} & \sigma_{33} & \sqrt{2}\sigma_{23} & - \sqrt{2}\sigma_{13} & \sqrt{2}\sigma_{12}\end{array}\right] -\f] -and, after transformation, a fourth order tensor \f$\mathbf{\mathfrak{C}}\f$ becomes -\f[ - \left[\begin{array}{cccccc} - C_{1111} & C_{1122} & C_{1133} & \sqrt{2}C_{1123} & \sqrt{2}C_{1113} & \sqrt{2}C_{1112}\\ - C_{1122} & C_{2222} & C_{2233} & \sqrt{2}C_{2223} & \sqrt{2}C_{2213} & \sqrt{2}C_{2212}\\ - C_{1133} & C_{2233} & C_{3333} & \sqrt{2}C_{3323} & \sqrt{2}C_{3313} & \sqrt{2}C_{3312}\\ - \sqrt{2}C_{1123} & \sqrt{2}C_{2223} & \sqrt{2}C_{3323} & 2C_{2323} & 2C_{2313} & 2C_{2312}\\ - \sqrt{2}C_{1113} & \sqrt{2}C_{2213} & \sqrt{2}C_{3313} & 2C_{2313} & 2C_{1313} & 2C_{1312}\\ - \sqrt{2}C_{1112} & \sqrt{2}C_{2212} & \sqrt{2}C_{3312} & 2C_{2312} & 2C_{1312} & 2C_{1212} - \end{array}\right]. -\f] -The inner product of two symmetric second order tensors \f$\mathbf{A}\f$ and \f$\mathbf{B}\f$ can be conveniently expressed in their Mandel notations \f$\hat{\mathbf{a}}\f$ and \f$\hat{\mathbf{b}}\f$ as -\f[ - \mathbf{A}:\mathbf{B}=\hat{\mathbf{a}}\cdot\hat{\mathbf{b}} -\f] - -which expresses the utility of this convention. - -Similarly, given the symmetric fourth order tensor \f$\mathbf{\mathfrak{C}}\f$ and its equivalent Mandel matrix \f$\hat{\mathbf{C}}\f$ contraction over two adjacent indices -\f[ - \mathbf{A}=\mathbf{\mathfrak{C}}:\mathbf{B} -\f] -simply becomes matrix-vector multiplication -\f[ - \hat{\mathbf{a}}=\hat{\mathbf{C}}\cdot\hat{\mathbf{b}}. -\f] - -The Mandel convention is relatively uncommon in finite element software, and so the user must be careful to convert back and forth from the Mandel convention to whichever convention the calling software uses. The Abaqus UMAT interface provided with NEML2 demonstrates how to make this conversion before and after each call. - -## Commonly used operators - -The deviator of a second order tensor is denoted: -\f[ - \operatorname{dev}\left(\mathbf{X}\right) = \mathbf{X} - \frac{1}{3} - \operatorname{tr}\left(\mathbf{X}\right) \mathbf{I} -\f] -with \f$\operatorname{tr}\f$ the trace and \f$\mathbf{I}\f$ the identity tensor. - -## Concatenation of flattened quantities - -When describing collections of objects the manual uses square brackets. For example, -\f[ - \left[ \begin{array}{ccc} s & \mathbf{X} & \mathbf{v} \end{array}\right] -\f] -indicates a collection of a scalar \f$s\f$, a vector representing a second order tensor \f$\mathbf{X}\f$ in Mandel notation, and a vector \f$\mathbf{v}\f$. This notation indicates the implementation is concatenating the quantities into a flat, 1D array (in this case with length \f$1 + 6 + 3 = 10\f$) preserving the original order. - diff --git a/doc/content/_02_user.md b/doc/content/_02_user.md new file mode 100644 index 0000000000..6aae98069b --- /dev/null +++ b/doc/content/_02_user.md @@ -0,0 +1,3 @@ +# User Guide {#user} + +[TOC] diff --git a/doc/content/_03_dev.md b/doc/content/_03_dev.md new file mode 100644 index 0000000000..23def9ffae --- /dev/null +++ b/doc/content/_03_dev.md @@ -0,0 +1,533 @@ +# Developer Guide {#dev} + +[TOC] + +## Tensor types {#tensor-types} + +Currently, PyTorch is the only supported tensor backend in NEML2. Therefore, all tensor types in NEML2 directly inherit from `torch::Tensor`. In the future, support for other tensor backends may be added, but the public-facing interfaces will remain largely the same. + +### Dynamically shaped tensor {#dynamically-shaped-tensor} + +[BatchTensor](@ref neml2::BatchTensor) is a general-purpose *dynamically shaped* tensor type for batched tensors. With a view towards vectorization, the same set of operations can be "simultaneously" applied to a "batch" of (logically the same) tensors. To provide a unified user interface for dealing with such batched operation, NEML2 assumes that the *first* \f$N\f$ dimensions of a tensor are batched dimensions, and the following dimensions are the base (logical) dimensions. + +> Unlike PyTorch, NEML2 explicitly distinguishes between batch dimensions and base (logical) dimensions. + +A `BatchTensor` can be created using +```cpp +BatchTensor A(torch::rand({1, 1, 5, 2}), 2); +``` +where `A` is a tensor with 2 batch dimensions. The batch sizes of `A` is `(1, 1)`: +```cpp +auto batch_sz = A.batch_sizes(); +neml2_assert(batch_sz == {1, 1}); +``` +and the base (logical) sizes of `A` is `(5, 2)`: +```cpp +auto base_sz = A.base_sizes(); +neml2_assert(batch_sz == {5, 2}); +``` + +### Statically shaped tensors {#statically-shaped-tensor} + +[FixedDimTensor](@ref neml2::FixedDimTensor) is the parent class for all the tensor types with a *fixed* base shape. It is templated on the base shape of the tensor. NEML2 offers a rich collection of primitive tensor types inherited from `FixedDimTensor`. Currently implemented primitive tensor types are summarized below + +| Tensor type | Base shape | Description | +| :------------------------------------- | :---------------- | :--------------------------------------------------------------- | +| [Scalar](@ref neml2::Scalar) | \f$()\f$ | Rank-0 tensor, i.e. scalar | +| [Vec](@ref neml2::Vec) | \f$(3)\f$ | Rank-1 tensor, i.e. vector | +| [R2](@ref neml2::R2) | \f$(3,3)\f$ | Rank-2 tensor | +| [SR2](@ref neml2::SR2) | \f$(6)\f$ | Symmetric rank-2 tensor | +| [WR2](@ref neml2::WR2) | \f$(3)\f$ | Skew-symmetric rank-2 tensor | +| [R3](@ref neml2::R3) | \f$(3,3,3)\f$ | Rank-3 tensor | +| [SFR3](@ref neml2::SFR3) | \f$(6,3)\f$ | Rank-3 tensor with symmetry on base dimensions 0 and 1 | +| [R4](@ref neml2::R4) | \f$(3,3,3,3)\f$ | Rank-4 tensor | +| [SSR4](@ref neml2::SSR4) | \f$(6,6)\f$ | Rank-4 tensor with minor symmetry | +| [R5](@ref neml2::R5) | \f$(3,3,3,3,3)\f$ | Rank-5 tensor | +| [SSFR5](@ref neml2::SSFR5) | \f$(6,6,3)\f$ | Rank-5 tensor with minor symmetry on base dimensions 0-3 | +| [Rot](@ref neml2::Rot) | \f$(3)\f$ | Rotation tensor represented in the Rodrigues form | +| [Quarternion](@ref neml2::Quarternion) | \f$(4)\f$ | Quarternion | +| [MillerIndex](@ref neml2::MillerIndex) | \f$(3)\f$ | Crystal direction or lattice plane represented as Miller indices | + +Furthermore, all primitive tensor types can be "registered" as variables on a `LabeledAxis`, which will be discussed in the following section on [labeled view](@ref tensor-labeling). + +## Working with tensors {#working-with-tensors} + +### Tensor creation {#tensor-creation} + +A factory tensor creation function produces a new tensor. All factory functions adhere to the same schema: +```cpp +::(, const torch::TensorOptions & options); +``` +where `` is the class name of the primitive tensor type listed above, and `` is the name of the factory function which produces the new tensor. `` are any required or optional arguments a particular factory function accepts. Refer to each tensor type's class documentation for the concrete signature. The last argument `const torch::TensorOptions & options` configures the data type, device, layout and other "meta" properties of the produced tensor. The commonly used meta properties are +- `dtype`: the data type of the elements stored in the tensor. Available options are `kUInt8`, `kInt8`, `kInt16`, `kInt32`, `kInt64`, `kFloat32`, and `kFloat64`. +- `layout`: the striding of the tensor. Available options are `kStrided` (dense) and `kSparse`. +- `device`: the compute device where the tensor will be allocated. Available options are `kCPU` and `kCUDA`. +- `requires_grad`: whether the tensor is part of a function graph used by automatic differentiation to track functional relationship. Available options are `true` and `false`. + +For example, the following code +```cpp +auto a = SR2::zeros({5, 3}, + torch::TensorOptions() + .device(torch::kCPU) + .layout(torch::kStrided) + .dtype(torch::kFloat32)); +``` +creates a statically (base) shaped, dense, single precision tensor of type `SR2` filled with zeros, with batch shape \f$(5, 3)\f$, allocated on the CPU. + +### Tensor broadcasting {#tensor-broadcasting} + +Quoting Numpy's definition of broadcasting: + +> The term broadcasting describes how NumPy treats arrays with different shapes during arithmetic operations. Subject to certain constraints, the smaller array is “broadcast” across the larger array so that they have compatible shapes. + +NEML2's broadcasting semantics is largely the same as those of Numpy and PyTorch. However, since NEML2 explicitly distinguishes between batch and base dimensions, the broadcasting semantics must also be extended. Two NEML2 tensors are said to be _batch-broadcastable_ if iterating backward from the last batch dimension, one of the following is satisfied: +1. Both tensors have the same size on the dimension; +2. One tensor has size 1 on the dimension; +3. The dimension does not exist in one tensor. + +_Base-broadcastable_ follows a similar definition. Most binary operators on dynamically shaped tensors, i.e., those of type `BatchTensor`, require the operands to be both batch- _and_ base-broadcastable. On the other hand, most binary operators on statically base shaped tensors, i.e., those of pritimitive tensor types, only require the operands to be batch-broadcastable. + +### Tensor indexing {#tensor-indexing} + +In defining the forward operator of a material model, many logically different tensors representing inputs, outputs, residuals, and Jacobians have to be created, copied, and destroyed on the fly. These operations occupy a significant amount of computing time, especially on GPUs. + +To address this challenge, NEML2 creates *views*, instead of copies, of tensors whenever possible. As its name suggests, the view of a tensor is a possibly different interpretation of the underlying data. Quoting the PyTorch documentation: + +> For a tensor to be viewed, the new view size must be compatible with its original size and stride, i.e., each new view dimension must either be a subspace of an original dimension, or only span across original dimensions \f$d, d+1, ..., d+k\f$ that satisfy the following contiguity-like condition that \f$\forall i = d,...,d+k-1\f$, +> \f[ +> \text{stride}[i] = \text{stride}[i+1] \times \text{size}[i+1] +> \f] +> Otherwise, it will not be possible to view self tensor as shape without copying it. + +In NEML2, use [base_index](@ref neml2::BatchTensorBase::base_index) for indexing the base dimensions and [batch_index](@ref neml2::BatchTensorBase::batch_index) for indexing the batch dimensions: +```cpp +using namespace torch::indexing; +BatchTensor A(torch::tensor({{2, 3, 4}, {-1, -2, 3}, {6, 9, 7}}), 1); +// A = [[ 2 3 4] +// [ -1 -2 3] +// [ 6 9 7]] +BatchTensor B = A.batch_index({Slice(0, 2)}); +// B = [[ 2 3 4] +// [ -1 -2 3]] +BatchTensor C = A.base_index({Slice(1, 3)}); +// C = [[ 3 4] +// [ -2 3] +// [ 9 7]] +``` +To modify the content of a tensor, use [base_index_put](@ref neml2::BatchTensorBase::base_index_put) or [batch_index_put](@ref neml2::BatchTensorBase::batch_index_put): +```cpp +A.base_index_put({Slice(1, 3)}, torch::ones({3, 2})); +// A = [[ 2 1 1] +// [ -1 1 1] +// [ 6 1 1]] +A.batch_index_put({Slice(0, 2)}, torch::zeros({2, 3})); +// A = [[ 0 0 0] +// [ 0 0 0] +// [ 6 1 1]] +``` +A detailed explanation on tensor indexing APIs is available as part of the official [PyTorch documentation](https://pytorch.org/cppdocs/notes/tensor_indexing.html). + +### Tensor labeling {#tensor-labeling} + +In the context of material modeling, oftentimes views of tensors have practical/physical meanings. For example, given a logically 1D tensor with base size 9, its underlying data in an arbitrary batch may look like +``` +equivalent plastic strain 2.1 + cauchy stress -2.1 + 0 + 1.3 + -1.1 + 2.5 + 2.5 + temperature 102.9 + time 3.6 +``` +where component 0 stores the scalar-valued equivalent plastic strain, components 1-6 store the tensor-valued cauchy stress (we use the Mandel notation for symmetric second order tensors), component 7 stores the scalar-valued temperature, and component 8 stores the scalar-valued time. + +The string indicating the physical meaning of the view, e.g., "cauchy stress", is called a "label", and the view of the tensor indexed by a label is called a "labeled view", i.e., +``` + cauchy stress -2.1 + 0 + 1.3 + -1.1 + 2.5 + 2.5 +``` + +NEML2 provides a data structure named [LabeledAxis](@ref neml2::LabeledAxis) to facilitate the creation and modification of labels, and a data structure named [LabeledTensor](@ref neml2::LabeledTensor) to facilitate the creation and modification of labeled views. + +The [LabeledAxis](@ref neml2::LabeledAxis) contains all information regarding how an axis of a `LabeledTensor` is labeled. The following naming convention is used: +- Item: A labelable slice of data +- Variable: An item that is also of a [NEML2 primitive tensor type](@ref tensor-types) +- Sub-axis: An item of type `LabeledAxis` + +So yes, an axis can be labeled recursively, e.g., + +``` + 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 +/// |-----------| |-----| | | | | | | +/// a b | | | | | | +/// |-------------------| |--------------| |--| |--| +/// sub a b c +``` +The above example represents an axis of size 15. This axis has 4 items: `a`, `b`, `c`, and `sub`. +- "a" is a variable of storage size 6 (possibly of type `SR2`). +- "b" is a variable of type `Scalar`. +- "c" is a variable of type `Scalar`. +- "sub" is a sub-axis of type `LabeledAxis`. "sub" by itself represents an axis of size 7, containing 2 items: + - "a" is a variable of storage size 6. + - "b" is a variable of type `Scalar`. + +Duplicate labels are *not* allowed on the same level of the axis, e.g. "a", "b", "c", and "sub" share the same level and so must be different. However, items on different levels of an axis can share the same label, e.g., "a" on the sub-axis "sub" has the same label as "a" on the main axis. In NEML2 convention, item names are always fully qualified, and a sub-axis is prefixed with a left slash, e.g. item "b" on the sub-axis "sub" can be denoted as "sub/b" on the main axis. + +> A label cannot contain: white spaces, quotes, left slash (`/`), or new line. +> +> Due to performance considerations, a `LabeledAxis` can only be modified, e.g., adding/removing variables and sub-axis, at the time a model is constructed. After the model construction phase, the `LabeledAxis` associated with that model can no longer be modified over the entire course of the simulation. + +Refer to the documentation for a complete list of APIs for creating and modifying a [LabeledAxis](@ref neml2::LabeledAxis). + +[LabeledTensor](@ref neml2::LabeledTensor) is the primary data structure in NEML2 for working with labeled tensor views. Each `LabeledTensor` consists of one `BatchTensor` and one or more `LabeledAxis`s. The `LabeledTensor` is templated on the base dimension \f$D\f$. [LabeledVector](@ref neml2::LabeledVector) and [LabeledMatrix](@ref neml2::LabeledMatrix) are the two most widely used data structures in NEML2. + +`LabeledTensor` handles the creation, modification, and accessing of labeled tensors. Recall that all primitive data types in a labeled tensor are flattened, e.g., a symmetric fourth order tensor of type `SSR4` with batch size `(5)` and base size `(6, 6)` are flattened to have base size `(36)` in the labeled tensor. The documentation provides a complete list of APIs. The commonly used methods are +- [operator()](@ref neml2::LabeledTensor::operator()()) for retrieving a labeled view into the raw (flattened) data without reshaping +- [get](@ref neml2::LabeledTensor::get) for retrieving a labeled view and reshaping it to the correct shape +- [set](@ref neml2::LabeledTensor::set) for setting values for a labeled view +- [slice](@ref neml2::LabeledTensor::slice) for slicing a sub-axis along a specific base dimension +- [block](@ref neml2::LabeledTensor::block) for sub-indexing the `LabeledTensor` with \f$D\f$ sub-axis names + +## Model {#model} + +### Model definition {#model-definition} + +A NEML2 model is a function (in the context of mathematics) +\f[ + f: \mathbb{R}^m \to \mathbb{R}^n +\f] +mapping from the input space \f$\mathbb{R}^m\f$ of dimension \f$m\f$ to the output space \f$\mathbb{R}^n\f$ of dimension \f$n\f$. \f$\left[ \cdot \right]\f$ be the flatten-concatenation operator, the input vector is the concatenation of \f$p\f$ flattened variables, i.e., +\f[ + x = \left[ x_i \right]_{i=1}^p \in \mathbb{R}^m, \quad \sum_{i=1}^p \lvert x_i \rvert = m, +\f] +where \f$\lvert x \rvert\f$ denotes the modulus of flattened variable \f$x\f$. Similarly, the output vector is the concatenation of \f$q\f$ flattened variables, i.e., +\f[ + y = \left[ y_i \right]_{i=1}^q \in \mathbb{R}^n, \quad \sum_{i=1}^q \lvert y_i \rvert = n. +\f] + +Translating the above mathematical definition into NEML2 is straightforward. +- A model following this definition derives from [Model](@ref neml2::Model). +- [declare_input_variable](@ref neml2::Model::declare_input_variable) declares an input variable \f$x_i\f$ in the input space \f$\mathbb{R}^m\f$. +- [declare_output_variable](@ref neml2::Model::declare_output_variable) declares an output variable \f$y_i\f$ in the output space \f$\mathbb{R}^n\f$. +- [set_value](@ref neml2::Model::set_value) is a method defining the forward operator \f$f\f$ itself. + +Both `declare_input_variable` and `declare_output_variable` are templated on the variable type -- recall that only a variable of the NEML2 primitive tensor type can be registered. Furthermore, both methods return a `Variable &` used for retrieving and setting the variable value inside the forward operator, i.e. `set_value`. Note that the reference returned by `declare_input_variable` is writable, while the reference returned by `declare_output_variable` is read-only. + +### Model composition {#model-composition} + +Quoting [Wikipedia](https://en.wikipedia.org/wiki/Function_composition): +> In mathematics, function composition is an operation \f$\circ\f$ that takes two functions \f$f\f$ and \f$g\f$, and produces a function \f$h = g \circ f\f$ such that \f$h(x) = g(f(x))\f$. + +Since NEML2 `Model` is a function (in the context of mathematics) at its core, it should be possible, in theory, to compose different NEML2 `Model`s into a new NEML2 `Model`. The [ComposedModel](@ref neml2::ComposedModel) is precisely for that purpose. + +Similar to the statement "a composed function is a function" in the context of mathematics, in NEML2, the equivalent statement "a `ComposedModel` is a `Model`" also holds. In addition, the `ComposedModel` provides four key features to help simplify the composition and reduces computational cost: +- Automatic dependency registration +- Automatic input/output identification +- Automatic dependency resolution +- Automatic chain rule + +### A symbolic example {#a-symbolic-example} + +To demonstrate the utility of the four key features of `ComposedModel`, let us consider the composition of three functions \f$f\f$, \f$g\f$, and \f$h\f$: +\f{align*} + y_1 &= f(x_1, x_2), \\ + y_2 &= g(y_1, x_3), \\ + y &= h(y_1, y_2, x_4). +\f} + +### Automatic dependency registration {#automatic-dependency-registration} + +It is obvious to us that the function \f$h\f$ _depends_ on functions \f$f\f$ and \f$g\f$ because the input of \f$h\f$ depends on the outputs of \f$f\f$ and \f$g\f$. Such dependency is automatically identified and registered while composing a `ComposedModel` in NEML2. This procedure is called "automatic dependency registration". + +In order to identify dependencies among different `Model`s, we keep track of the set of _consumed_ variables, \f$\mathcal{I}_i\f$, and a set of _provided_ variables, \f$\mathcal{O}_i\f$, for each `Model` \f$f_i\f$. When a set of models (functions) are composed together, `Model` \f$f_i\f$ is said to _depend_ on \f$f_j\f$ if and only if \f$\exists x\f$ such that +\f[ + x \in \mathcal{I}_i \wedge x \in \mathcal{O}_j. +\f] + +### Automatic input/output identification {#automatic-input-output-identification} + +The only possible composition \f$r\f$ of these three functions is +\f[ + y = r(x_1, x_2, x_3, x_4) := h(f(x_1, x_2), g(f(x_1, x_2), x_3), x_4). +\f] +The input variables of the composed function \f$r\f$ are \f$[x_1, x_2, x_3, x_4]\f$ (or their flattened concatenation), and the output variable of the composed function is simply \f$y\f$. The input/output variables are automatically identified while composing a `ComposedModel` in NEML2. This procedure is referred to as "automatic input/output identification". + +In a `ComposedModel`, a _leaf_ model is a model which does not depend on any other model, and a _root_ model is a model which is not depent upon by any other model. A `ComposedModel` may have multiple leaf models and multiple root models. An input variable is said to be a _root_ input variable if it is not consumed by any other model, i.e. \f$x \in \mathcal{I}_i\f$ is a root input variable if and only if +\f[ + x \notin \mathcal{O}_j, \quad \forall i \neq j. +\f] +Similarly, an output variable is said to be a _leaf_ output variable if it is not provided by any other model, i.e. \f$x \in \mathcal{O}_i\f$ is a leaf output variable if an only if +\f[ + x \notin \mathcal{I}_j, \quad \forall i \neq j. +\f] +The input variables of a `ComposedModel` is the union of the set of all the root input variables, and the output variables of a `ComposedModel` is the set of all the leaf output variables. + +### Automatic dependency resolution {#automatic-dependency-resolution} + +To evaluate the forward operator of the composed model \f$r\f$, one has to first evaluate model \f$f\f$, then model \f$g\f$, and finally model \f$h\f$. The process of sorting out such evaluation order is called "dependency resolution". + +While it is possible to sort the evaluation order "by hand" for this simple example composition, it is generally not a trivial task for practical compositions with more involved dependencies. To that end, NEML2 uses [topological sort](https://en.wikipedia.org/wiki/Topological_sorting) to sort the model evaluation order, such that by the time each model is evaluated, all of its dependent models have already been evaluated. + +### Automatic chain rule {#automatic-chain-rule} + +Chain rule can be applied to evaluate the derivative of the forward operator with respect to the input variables, i.e., +\f{align*} + \frac{\partial y}{\partial x_1} &= \left( \frac{\partial y}{\partial y_1} + \frac{\partial y}{\partial y_2} \frac{\partial y_2}{\partial y_1} \right) \frac{\partial y_1}{\partial x_1}, \\ + \frac{\partial y}{\partial x_2} &= \left( \frac{\partial y}{\partial y_1} + \frac{\partial y}{\partial y_2} \frac{\partial y_2}{\partial y_1} \right) \frac{\partial y_1}{\partial x_2}, \\ + \frac{\partial y}{\partial x_3} &= \frac{\partial y}{\partial y_2} \frac{\partial y_2}{\partial x_3}, \\ + \frac{\partial y}{\partial x_4} &= \frac{\partial y}{\partial x_4}. +\f} +Spelling out this chain rule can be cumbersome and error-prone, especially for more complicated model compositions. The evaluation of the chain rule is automated in NEML2, and the user is only responsible for implementing the partial derivatives of each model. For example, in the implementation of `Model` \f$f\f$, the user only need to define the partial derivatives +\f[ + \frac{\partial y_1}{\partial x_1}, \quad \frac{\partial y_1}{\partial x_2}; +\f] +similarly, `Model` \f$g\f$ only defines +\f[ + \frac{\partial y_2}{\partial y_1}, \quad \frac{\partial y_2}{\partial x_3} +\f] +and `Model` \f$h\f$ only defines +\f[ + \frac{\partial y}{\partial y_1}, \quad \frac{\partial y}{\partial y_2}, \quad \frac{\partial y}{\partial x_4}. +\f] +The assembly of the partial derivatives into the total derivative \f$\partial y / \partial \mathbf{x}\f$ using the chain rule is handled by NEML2. This design serves as the fundation for a modular model implementation: +- Each model _does not_ need to know its composition with others. +- The same model partial derivatives can be reused in _any_ composition. + +## Developing custom model {#custom-model} + +The following tutorials serve as a developer-facing step-by-step guide for creating and testing a custom material model. A simple linear isotropic hardening is used as the example in this tutorial. The model can be mathematically written as +\f{align*} + k &= H \bar{\varepsilon}^p, +\f} +where \f$\bar{\varepsilon}^p\f$ is the equivalent plastic strain and \f$k\f$ is the isotropic hardening. The input variable for this model is +\f$\mathbf{\varepsilon}\f$, the output variable for this model is \f$k\f$, and the parameters of the model is \f$H\f$. + +### Naming conventions {#naming-conventions} + +Recall that NEML2 models operates on _labeled tensors_, and that the collection of labels (with their corresponding layout) is called an labeled axis ([LabeledAxis](@ref neml2::LabeledAxis)). NEML2 predefines 5 sub-axes to categorize all the input, output and intermediate variables: +- State \f$\mathcal{S}\f$: Variables on the "state" sub-axis collectively characterize the current _state_ of the material subject to given external forces. The state variables are usually the output of a physically meaningful material model. +- Forces \f$\mathcal{F}\f$: Variables on the "forces" sub-axis define the _external_ forces that drive the response of the material. +- Old state \f$\mathcal{S}_n\f$: The state variables _prior to_ the current material update. In the time-discrete setting, these are the state variables from the previous time step. +- Old forces \f$\mathcal{F}_n\f$: The external forces _prior to_ the current material update. In the time-discrete setting, these are the forces from the previous time step. +- Residual \f$\mathcal{R}\f$: The residual defines an _implicit_ model/function. An implicit model is updated by solving for the state variables that result in zero residual. + +In NEML2, the following naming conventions are recommended: +- User-facing variables and option names should be _as descriptive as possible_. For example, the equivalent plastic strain is named "equivalent_plastic_strain". Note that white spaces, quotes, and left slashes are not allowed in the names. Underscores are recommended as an replacement for white spaces. +- Developer-facing variables and option names should use simple alphanumeric symbols. For example, the equivalent plastic strain is named "ep" in consistency with most of the existing literature. +- Developner-facing member variables and option names should use the same alphanumeric symbols. For example, the member variable for the equivalent plastic strain is named `ep`. However, if the member variable is protected or private, it is recommended to prefix it with an underscore, i.e. `_ep`. +- Struct names and class names should use `PascalCase`. +- Function names should use `snake_case`. + +### Declaring variables {#declaring-variables} + +The development of every model begins with the declaration and registration of its input and output variables. Here, we first define an abstract base class that will be later used to define the linear isotropic hardening relation. The abstract base class defines the isotropic hardening relation +\f[ + k = f\left( \bar{\varepsilon}^p \right), +\f] +mapping the equivalent plastic strain to the isotropic hardening. The base class is named `IsotropicHardening` following the [naming conventions](@ref naming-conventions). The header file [IsotropicHardening.h](@ref neml2::IsotropicHardening) is displayed below +```cpp +#pragma once + +#include "neml2/models/Model.h" + +namespace neml2 +{ +class IsotropicHardening : public Model +{ +public: + static OptionSet expected_options(); + + IsotropicHardening(const OptionSet & options); + +protected: + /// Equivalent plastic strain + const Variable & _ep; + + /// Isotropic hardening + Variable & _h; +}; +} // namespace neml2 +``` +Since isotropic hardening _is a_ model, the class inherits from `Model`. The user-facing expected options are defined by the static method `expected_options`. NEML2 handles the parsing of user-specified options and pass them to the constructor. The input variable of the model is the equivalent plastic strain, and the output variable of the model is the isotropic hardening. Their corresponding variable value references are stored as `_ep` and `_h`, respectively, again following the [naming conventions](@ref naming-conventions). + +The expected options and the constructor are defined as +```cpp +#include "neml2/models/solid_mechanics/IsotropicHardening.h" + +namespace neml2 +{ +OptionSet +IsotropicHardening::expected_options() +{ + OptionSet options = Model::expected_options(); + options.set("equivalent_plastic_strain") = VariableName("state", "internal", "ep"); + options.set("isotropic_hardening") = VariableName("state", "internal", "k"); + return options; +} + +IsotropicHardening::IsotropicHardening(const OptionSet & options) + : Model(options), + _ep(declare_input_variable("equivalent_plastic_strain")), + _h(declare_output_variable("isotropic_hardening")) +{ +} +} // namespace neml2 +``` +Recall that variable names on `LabeledAxis` are always fully qualified, the equivalent plastic strain and the isotropic hardening are denoted as "state/internal/ep" and "state/internal/k", respectively. An instance of the class is constructed by extracting user-specified options (of type `OptionSet`). Note how `declare_input_variable` and `declare_output_variable` are used to declare and register the input and output variables. + +### Declaring parameters {#declaring-parameters} + +Now that the abstract base class `IsotropicHardening` has been implemented, we are ready to define our first concrete NEML2 model that describes a linear isotropic hardening relation +\f[ + k = H \bar{\varepsilon}^p. +\f] +Note that \f$H\f$ is a model parameter. Following the naming convention, the concrete class is named `LinearIsotropicHardening`. The header file is displayed below. +```cpp +#pragma once + +#include "neml2/models/solid_mechanics/IsotropicHardening.h" + +namespace neml2 +{ +/** + * @brief Simple linear map between equivalent strain and hardening + * + */ +class LinearIsotropicHardening : public IsotropicHardening +{ +public: + static OptionSet expected_options(); + + LinearIsotropicHardening(const OptionSet & options); + +protected: + void set_value(bool out, bool dout_din, bool d2out_din2) override; + + const Scalar & _K; +}; +} // namespace neml2 +``` +It derives from the abstract base class `IsotropicHardening` and implements the method `set_value` as the forward operator. The model parameter \f$H\f$ is stored as a protected member variable `_K`. The model implementation is shown below. +```cpp +#include "neml2/models/solid_mechanics/LinearIsotropicHardening.h" + +namespace neml2 +{ +register_NEML2_object(LinearIsotropicHardening); + +OptionSet +LinearIsotropicHardening::expected_options() +{ + OptionSet options = IsotropicHardening::expected_options(); + options.set>("hardening_modulus"); + return options; +} + +LinearIsotropicHardening::LinearIsotropicHardening(const OptionSet & options) + : IsotropicHardening(options), + _K(declare_parameter("K", "hardening_modulus")) +{ +} + +void +LinearIsotropicHardening::set_value(bool out, bool dout_din, bool d2out_din2) +{ + if (out) + _h = _K * _ep; + + if (dout_din) + _h.d(_ep) = _K; + + if (d2out_din2) + { + // zero + } +} +} // namespace neml2 +``` +Note that an additional option named "hardening_modulus" is requested from the user. The model parameter is registered using the API `declare_parameter`. In the `set_value` method, the current value of the input variable, equivalent plastic strain, is stored in the member `_ep`, and so the isotropic hardening can be computed as +```cpp +_K * _ep +``` +The computed result is copied into the model output variable `_h` by +```cpp +_h = _K * _ep; +``` +In addition, the first derivative of the forward operator is defined as +```cpp +_h.d(_ep) = _K; +``` +Last but not the least, the model is registed in the NEML2 model factory using the macro +```cpp +register_NEML2_object(LinearIsotropicHardening); +``` +so that an instance of the class can be created at runtime. + +### Testing {#testing} + +It is of paramount importance to ensure the correctness of the implementation. NEML2 offers 5 types of tests with different purposes. + +A Catch2 test refers to a test directly written in C++ source code within the Catch2 framework. It offers the highest level of flexibility, but requires more effort to set up. To understand how a Catch2 test works, please refer to the [official Catch2 documentation](https://github.com/catchorg/Catch2/blob/v2.x/docs/tutorial.md). + +A model unit test examines the outputs of a `Model` given a predefined set of inputs. Model unit tests can be directly designed using the input file syntax with the `ModelUnitTest` type. A variety of checks can be turned on and off based on input file options. To list a few: `check_first_derivatives` compares the implemented first order derivatives of the model against finite-differencing results, and the test is marked as passing only if the two derivatives are within tolerances specified with `derivative_abs_tol` and `derivative_rel_tol`; if `check_cuda` is set to `true`, all checks are repeated twice, once on CPU and once on GPU (if available), and pass only if the two evaluations yield same results within tolerances. + +All input files for model unit tests should be stored inside `tests/unit/models`. Every input file with the `.i` extension will be automatically discovered and executed. To run all the model unit tests, use the following commands +``` +cd tests +./unit_tests models +``` + +To run a specific model unit test, use the `-c` command line option followed by the relative location of the input file, i.e. +``` +cd tests +./unit_tests models -c solid_mechanics/LinearIsotropicElasticity.i +``` + +A model regression test runs a `Model` using a user specified driver. The results are compared against a predefined reference (stored on the disk checked into the repository). The test passes only if the current results are the same as the predefined reference (again within specified tolerances). The regression tests ensure the consistency of implementations across commits. Currently, `TransientRegression` is the only supported type of regression test. + +Each input file for model regression tests should be stored inside a separate folder inside `tests/regression`. Every input file with the `.i` extension will be automatically discovered and executed. To run all the model regression tests, use the `regression_tests` executable followed by the physics module, i.e. +``` +cd tests +./regression_tests "solid mechanics" +``` +To run a specific model regression test, use the `-c` command line option followed by the relative location of the input file, i.e. +``` +cd tests +./regression_tests "solid mechanics" -c viscoplasticity/chaboche/model.i +``` +Note that the regression test expects an option `reference` which specifies the relative location to the reference solution. + +The model verification test is similar to the model regression test in terms of workflow. The difference is the a verification test defines the reference solution using NEML, the predecessor of NEML2. Since NEML was developed with strict software assurance, the verification tests ensure that the migration from NEML to NEML2 does not cause any regression in software quality. + +Each input file for model verification tests should be stored inside a separate folder inside `tests/verification`. Every input file with the `.i` extension will be automatically discovered and executed. To run all the model verification tests, use the `verification_tests` executable followed by the physics module, i.e. +``` +cd tests +./verification_tests "solid mechanics" +``` + +To run a specific model verification test, use the `-c` command line option followed by the relative location of the input file, i.e. +``` +cd tests +./verification_tests "solid mechanics" -c chaboche/chaboche.i +``` +The regression test compares variables (specified using the `variables` option) against reference values (specified using the `references` option). The reference variables can be read using input objects with type `VTestTimeSeries`. + +### Benchmarking {#benchmarking} + +The benchmark tests can be authored within the [Catch2 microbenchmarking framework](https://github.com/catchorg/Catch2/blob/v2.x/docs/benchmarks.md). Before any benchmarks can be executed, the clock's resolution is estimated. A few other environmental artifacts are also estimated at this point, like the cost of calling the clock function, but they almost never have any impact in the results. The user code is executed a few times to obtain an estimate of the amount of runs that should be in each sample. This also has the potential effect of bringing relevant code and data into the caches before the actual measurement starts. Finally, all the samples are collected sequentially by performing the number of runs estimated in the previous step for each sample. + +To run a benchmark test, use the `benchmark.sh` script inside the `scripts` directory with 3 positional arguments: +``` +./scripts/benchmark.sh Chaboche 5 timings +``` +The first positional argument specifies the name of the benchmark test to run. The second positional argument specifies the number of samples to repeat in each iteration. The third positional argument specifies the output directory of the benchmark results. + +The Chaboche benchmark test is repeated with different batch sizes and on different devices (in this case CPU and GPU). The final benchmark results are summarized in the following figure. + +![Chaboche benchmark results](@ref timings.png){html: width=50%, latex: width=10cm} diff --git a/doc/content/_03_impl.md b/doc/content/_03_impl.md deleted file mode 100644 index bff46426a3..0000000000 --- a/doc/content/_03_impl.md +++ /dev/null @@ -1,270 +0,0 @@ -# Implementation {#impl} - -[TOC] - -## NEML2 tensor types {#primitive} - -Currently, libTorch is the only supported tensor backend in NEML2. Therefore, all tensor types in NEML2 directly inherit from `torch::Tensor`. In the future, support for other tensor backend libraries may be added, but the public-facing interfaces will remain largely the same. - -### BatchTensor - -[BatchTensor](@ref neml2::BatchTensor) is a general purpose tensor type for batched `torch::Tensor`s. With a view towards vectorization, the same set of operations can be "simultaneously" applied to a large "batch" of (logically the same) tensors. To provide a unified user interface for dealing with such batched operation, NEML2 assumes that the *first* \f$N\f$ dimensions of a tensor are batched dimensions, and the following dimensions are the base (logical) dimensions. - -> Unlike libTorch, NEML2 explicitly distinguishes between batch dimensions and base (logical) dimensions. - -The `BatchTensor` is templated on the number of batch dimensions \f$N\f$. Although the number of batched dimensions is known at compile time, the size of each dimension is not. The batch dimensions can be reshaped at runtime. For example, a `BatchTensor` can be created as -```cpp -BatchTensor<2> A = torch::rand({1, 1, 5, 2}); -``` -where `A` is a tensor with 2 batch dimensions. The batch sizes of `A` is `(1, 1)`: -```cpp -auto batch_sz = A.batch_sizes(); -// batch_sz == {1, 1} -``` -and the base (logical) sizes of `A` is `(5, 2)`: -```cpp -auto base_sz = A.base_sizes(); -// batch_sze == {5, 2} -``` -The base tensor can be reshaped (e.g., expanded and copied) at runtime along its batch dimensions using -```cpp -BatchTensor<2> B = A.batch_expand_copy({3, 4}); -auto new_batch_sz = B.batch_sizes(); -// new_batch_sz == {3, 4} -``` - -### FixedDimTensor - -[FixedDimTensor](@ref neml2::FixedDimTensor) inherits from `BatchTensor`. It is additionally templated on the sizes of the base dimensions. For example, -```cpp -static_assert(FixedDimTensor<2, 6>::const_base_sizes == {6}); -``` - -### Primitive tensor types - -All primitive tensor types inherit from `FixedDimTensor` with a *single* batch dimension. Currently implemented primitive tensor types include -- [Scalar](@ref neml2::Scalar), a (batched) scalar quantity derived from the specialization `FixedDimTensor<1, 1>` -- [SymR2](@ref neml2::SymR2), a (batched) symmetric second order tensor derived from the specialization `FixedDimTensor<1, 6>` -- [SymSymR4](@ref neml2::SymSymR4), a (batched) symmetric fourth order tensor derived from the specialization `FixedDimTensor<1, 6, 6>` - -Furthermore, all primitive tensor types can be "registered" as variables on a `LabeledAxis`, which will be discussed in the following section on [labeled view](@ref labeledview). - -## Tensor view and label {#labeledview} - -### Tensor view - -In defining the forward operator of a constitutive model, many logically different tensors representing inputs, outputs, residuals, and Jacobians have to be created, copied, and destroyed on the fly. These operations occupy a significant amount of computing time, especially on GPUs. - -To address this challenge, NEML2 creates *views*, instead of copies, of tensors whenever possible. As its name suggests, the view of a tensor is a possibly different interpretation of the underlying data. Quoting the PyTorch documentation: - -> For a tensor to be viewed, the new view size must be compatible with its original size and stride, i.e., each new view dimension must either be a subspace of an original dimension, or only span across original dimensions \f$d, d+1, ..., d+k\f$ that satisfy the following contiguity-like condition that \f$\forall i = d,...,d+k-1\f$, -> \f[ -> \text{stride}[i] = \text{stride}[i+1] \times \text{size}[i+1] -> \f] -> Otherwise, it will not be possible to view self tensor as shape without copying it. - -### Working with tensor views - -In NEML2, to index a view of a `BatchTensor`, use [base_index](@ref neml2::BatchTensor::base_index) for indexing the base dimensions and [batch_index](@ref neml2::BatchTensor::batch_index) for indexing the batch dimensions: -```cpp -BatchTensor<1, 3> A = torch::tensor({{2, 3, 4}, {-1, -2, 3}, {6, 9, 7}}); -// A = [[ 2 3 4] -// [ -1 -2 3] -// [ 6 9 7]] -using namespace torch::indexing; -BatchTensor<1, 3> B = A.batch_index({Slice(0, 2)}); -// B = [[ 2 3 4] -// [ -1 -2 3]] -BatchTensor<1, 3> C = A.base_index({Slice(1, 3)}); -// C = [[ 3 4] -// [ -2 3] -// [ 9 7]] -``` -To modify a view of a `BatchTensor`, use [base_index_put](@ref neml2::BatchTensor::base_index_put) or [batch_index_put](@ref neml2::BatchTensor::batch_index_put): -```cpp -A.base_index_put({Slice(1, 3)}, torch::ones({3, 2})); -// A = [[ 2 1 1] -// [ -1 1 1] -// [ 6 1 1]] -A.batch_index_put({Slice(0, 2)}, torch::zeros({2, 3})); -// A = [[ 0 0 0] -// [ 0 0 0] -// [ 6 1 1]] -``` -A detailed explanation on tensor indexing APIs is available as part of the official [PyTorch documentation](https://pytorch.org/cppdocs/notes/tensor_indexing.html). - -### Labeled tensor view - -In the context of constitutive modeling, often times views of tensors have practical/physical meanings. For example, given a logically 1D tensor with base size 9, its underlying data in an arbitrary batch may look like -``` -equivalent plastic strain 2.1 - cauchy stress -2.1 - 0 - 1.3 - -1.1 - 2.5 - 2.5 - temperature 102.9 - time 3.6 -``` -where component 0 stores the scalar-valued equivalent plastic strain, components 1-6 store the tensor-valued cauchy stress (recall that we use the [Mandel](@ref mandel) notation for symmetric second order tensors), component 7 stores the scalar-valued temperature, and component 8 stores the scalar-valued time. - -The string indicating the physical meaning of the view, e.g., "cauchy stress", is called a "label", and the view of the tensor indexed by a label is called a "labeled view", i.e., -``` - cauchy stress -2.1 - 0 - 1.3 - -1.1 - 2.5 - 2.5 -``` - -NEML2 provides a data structure named [LabeledAxis](@ref neml2::LabeledAxis) to facilitate the creation and modification of labels, and a data structure named [LabeledTensor](@ref neml2::LabeledTensor) to facilitate the creation and modification of labeled views. - -### LabeledAxis - -The [LabeledAxis](@ref neml2::LabeledAxis) contains all information regarding how an axis of a `LabeledTensor` is labeled. The following naming convention is used: -- Item: A labelable chunk of data -- Variable: An item that is also of a [NEML2 primitive tensor type](@ref primitive) -- Sub-axis: An item of type `LabeledAxis` - -So yes, an axis can be labeled recursively, e.g., - -``` - 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 -/// |-----------| |-----| | | | | | | -/// a b | | | | | | -/// |-------------------| |--------------| |--| |--| -/// sub a b c -``` -The above example represents an axis of size 15. This axis has 4 items: `a`, `b`, `c`, and `sub`. -- "a" is a variable of storage size 6 (possibly of type `SymR2`). -- "b" is a variable of type `Scalar`. -- "c" is a variable of type `Scalar`. -- "sub" is a sub-axis of type `LabeledAxis`. "sub" by itself represents an axis of size 7, containing 2 items: - - "a" is a variable of storage size 6. - - "b" is a variable of type `Scalar`. - -Duplicate labels are *not* allowed on the same level of the axis, e.g. "a", "b", "c", and "sub" share the same level and so must be different. However, items on different levels of an axis can share the same label, e.g., "a" on the sub-axis "sub" has the same label as "a" on the main axis. In NEML2 convention, item names are always fully qualified, and a sub-axis is prefixed with a left slash, e.g. item "b" on the sub-axis "sub" can be denoted as "sub/b" on the main axis. - -> A label cannot contain: white spaces, quotes, left slash (`/`), or new line. - -> Due to performance considerations, a `LabeledAxis` can only be modified, e.g., adding/removing variables and sub-axis, at the time a model is constructed. After the model construction phase, the `LabeledAxis` associated with that model can no longer be modified over the entire course of the simulation. - -Refer to the doxygen documentation for a complete list of APIs for creating and modifying a [LabeledAxis](@ref neml2::LabeledAxis). - -### LabeledTensor - -[LabeledTensor](@ref neml2::LabeledTensor) is the primary data structure in NEML2 for working with labeled tensor views. Each `LabeledTensor` consists of one `BatchTensor` and one or more `LabeledAxis`s. The `LabeledTensor` is templated on the batch dimension \f$N\f$ and the base dimension \f$D\f$. [LabeledVector](@ref neml2::LabeledVector) (derived from `LabeledTensor<1, 1>`) and [LabeledMatrix](@ref neml2::LabeledMatrix) (derived from `LabeledTensor<1, 2>`) are the two most widely used data structures in NEML2. - -`LabeledTensor` handles the creation, modification, and accessing of labeled tensors. Recall that all primitive data types in a labeled tensor are flattened, e.g., a symmetric fourth order tensor of type `SymSymR4` with batch size `(5)` and base size `(6, 6)` are flattened to have base size `(36)` in the labeled tensor. The doxygen documentation provides a complete list of APIs. The commonly used methods are -- [operator()](@ref neml2::LabeledTensor::operator()()) for retrieving a labeled view into the raw (flattened) data without reshaping -- [get](@ref neml2::LabeledTensor::get) for retrieving a labeled view and reshaping it to the correct shape -- [set](@ref neml2::LabeledTensor::set) for setting values for a labeled view -- [slice](@ref neml2::LabeledTensor::slice) for slicing a sub-axis along a specific base dimension -- [block](@ref neml2::LabeledTensor::block) for sub-indexing the `LabeledTensor` with \f$D\f$ sub-axis names - -## Model definition {#modeldefinition} - -A NEML2 model is a function (in the context of mathematics) -\f[ - f: \mathbb{R}^m \to \mathbb{R}^n -\f] -mapping from the input space \f$\mathbb{R}^m\f$ of dimension \f$m\f$ to the output space \f$\mathbb{R}^n\f$ of dimension \f$n\f$. Recall that \f$\left[ \cdot \right]\f$ is the [flatten-concatenation operator](@ref math). The input vector is the concatenation of \f$p\f$ flattened variables, i.e., -\f[ - x = \left[ x_i \right]_{i=1}^p \in \mathbb{R}^m, \quad \sum_{i=1}^p \lvert x_i \rvert = m, -\f] -where \f$\lvert x \rvert\f$ denotes the modulus of flattened variable \f$x\f$. Similarly, the output vector is the concatenation of \f$q\f$ flattened variables, i.e., -\f[ - y = \left[ y_i \right]_{i=1}^q \in \mathbb{R}^n, \quad \sum_{i=1}^q \lvert y_i \rvert = n. -\f] - -Translating the above mathematical definition into NEML2 is straightforward. -- A model following this definition derives from [Model](@ref neml2::Model). -- [declare_input_variable](@ref neml2::Model::declare_input_variable) declares an input variable \f$x_i\f$ in the input space \f$\mathbb{R}^m\f$. -- [declare_output_variable](@ref neml2::Model::declare_output_variable) declares an output variable \f$y_i\f$ in the output space \f$\mathbb{R}^n\f$. -- [set_value](@ref neml2::Model::set_value) is a method defining the forward operator \f$f\f$. - -Both `declare_input_variable` and `declare_output_variable` are templated on the variable type -- recall that only a variable of the NEML2 primitive tensor type can be registered. Furthermore, both calls return a convenient accessor of type [VariableName](@ref neml2::VariableName) which can be later used to retrieve/modify the labeled view of the input/output vector. - -> Declaration of the variables don't immediately set up the layout of the input/output `LabeledAxis`. The method [setup](@ref neml2::Model::setup) should be explicitly called in order to set up the memory layout of the `LabeledAxis`s. **Note that [setup](@ref neml2::Model::setup) must be called after all the variables have been added, and before the forward operator of the `Model` can be used.** - -## Model composition {#modelcomposition} - -Quoting [Wikipedia](https://en.wikipedia.org/wiki/Function_composition): -> In mathematics, function composition is an operation \f$\circ\f$ that takes two functions \f$f\f$ and \f$g\f$, and produces a function \f$h = g \circ f\f$ such that \f$h(x) = g(f(x))\f$. - -Since NEML2 `Model` is a function (in the context of mathematics) at its core, it should be possible, in theory, to compose different NEML2 `Model`s into a new NEML2 `Model`. The [ComposedModel](@ref neml2::ComposedModel) is precisely for that purpose. - -Similar to the statement "a composed function is a function" in the context of mathematics, in NEML2, the equivalent statement "a ComposedModel is a Model" also holds. In addition, the `ComposedModel` provides four key features to help simplify the composition and reduces computational cost: -- Automatic dependency registration -- Automatic input/output identification -- Automatic dependency resolution -- Automatic chain rule - -### A symbolic example - -To demonstrate the utility of the four key features of `ComposedModel`, let us consider the composition of three functions \f$f\f$, \f$g\f$, and \f$h\f$: -\f{align*} - y_1 &= f(x_1, x_2), \\ - y_2 &= g(y_1, x_3), \\ - y &= h(y_1, y_2, x_4). -\f} - -### Automatic dependency registration - -It is obvious to us that the function \f$h\f$ _depends_ on functions \f$f\f$ and \f$g\f$ because the input of \f$h\f$ depends on the outputs of \f$f\f$ and \f$g\f$. Such dependency is automatically identified and registered while composing a `ComposedModel` in NEML2. This procedure is called "automatic dependency registration". - -In order to identify dependencies among different `Model`s, we keep track of the set of _consumed_ variables, \f$\mathcal{I}_i\f$, and a set of _provided_ variables, \f$\mathcal{O}_i\f$, for each `Model` \f$f_i\f$. When a set of models (functions) are composed together, `Model` \f$f_i\f$ is said to _depend_ on \f$f_j\f$ if and only if \f$\exists x\f$ such that -\f[ - x \in \mathcal{I}_i \wedge x \in \mathcal{O}_j. -\f] - -### Automatic input/output identification - -The only possible composition \f$r\f$ of these three functions is -\f[ - y = r(x_1, x_2, x_3, x_4) := h(f(x_1, x_2), g(f(x_1, x_2), x_3), x_4). -\f] -The input variables of the composed function \f$r\f$ are \f$[x_1, x_2, x_3, x_4]\f$ (or their flattened concatenation), and the output variable of the composed function is simply \f$y\f$. The input/output variables are automatically identified while composing a `ComposedModel` in NEML2. This procedure is referred to as "automatic input/output identification". - -In a `ComposedModel`, a _leaf_ model is a model which does not depend on any other model, and a _root_ model is a model which is not depent upon by any other model. A `ComposedModel` may have multiple leaf models and multiple root models. An input variable is said to be a _root_ input variable if it is not consumed by any other model, i.e. \f$x \in \mathcal{I}_i\f$ is a root input variable if and only if -\f[ - x \notin \mathcal{O}_j, \quad \forall i \neq j. -\f] -Similarly, an output variable is said to be a _leaf_ output variable if it is not provided by any other model, i.e. \f$x \in \mathcal{O}_i\f$ is a leaf output variable if an only if -\f[ - x \notin \mathcal{I}_j, \quad \forall i \neq j. -\f] -The input variables of a `ComposedModel` is the union of the set of all the root input variables, and the output variables of a `ComposedModel` is the set of all the leaf output variables. - -### Automatic dependency resolution - -To evaluate the forward operator of the composed model \f$r\f$, one has to first evaluate model \f$f\f$, then model \f$g\f$, and finally model \f$h\f$. The process of sorting out such evaluation order is called "dependency resolution". - -While it is possible to sort the evaluation order "by hand" for this simple example composition, it is generally not a trivial task for practical compositions with more involved dependencies. To that end, NEML2 uses [topological sort](https://en.wikipedia.org/wiki/Topological_sorting) to sort the model evaluation order, such that by the time each model is evaluated, all of its dependent models have already been evaluated. - -### Automatic chain rule - -Chain rule can be applied to evaluate the derivative of the forward operator with respect to the input variables, i.e., -\f{align*} - \frac{\partial y}{\partial x_1} &= \left( \frac{\partial y}{\partial y_1} + \frac{\partial y}{\partial y_2} \frac{\partial y_2}{\partial y_1} \right) \frac{\partial y_1}{\partial x_1}, \\ - \frac{\partial y}{\partial x_2} &= \left( \frac{\partial y}{\partial y_1} + \frac{\partial y}{\partial y_2} \frac{\partial y_2}{\partial y_1} \right) \frac{\partial y_1}{\partial x_2}, \\ - \frac{\partial y}{\partial x_3} &= \frac{\partial y}{\partial y_2} \frac{\partial y_2}{\partial x_3}, \\ - \frac{\partial y}{\partial x_4} &= \frac{\partial y}{\partial x_4}. -\f} -Spelling out this chain rule can be cumbersome and error-prone, especially for more complicated model compositions. The evaluation of the chain rule is automated in NEML2, and the user is only responsible for implementing the partial derivatives of each model. For example, in the implementation of `Model` \f$f\f$, the user only need to define the partial derivatives -\f[ - \frac{\partial y_1}{\partial x_1}, \quad \frac{\partial y_1}{\partial x_2}; -\f] -similarly, `Model` \f$g\f$ only defines -\f[ - \frac{\partial y_2}{\partial y_1}, \quad \frac{\partial y_2}{\partial x_3} -\f] -and `Model` \f$h\f$ only defines -\f[ - \frac{\partial y}{\partial y_1}, \quad \frac{\partial y}{\partial y_2}, \quad \frac{\partial y}{\partial x_4}. -\f] -The assembly of the partial derivatives into the total derivative \f$\partial y / \partial \mathbf{x}\f$ using the chain rule is handled by NEML2. This design serves as the fundation for a modular model implementation: -- Each model _does not_ need to know its composition with others. -- The same model partial derivatives can be reused in _any_ composition. diff --git a/doc/content/_04_devel.md b/doc/content/_04_devel.md deleted file mode 100644 index 297aef0db3..0000000000 --- a/doc/content/_04_devel.md +++ /dev/null @@ -1,278 +0,0 @@ -# Model Development {#devel} - -[TOC] - -This tutorial serves as a developer-facing step-by-step guide for creating, testing, and using a constitutive model. A simple small deformation \f$J_2\f$ viscoplasticity model with isotropic hardening is used as the example in this tutorial. For convenience, the mathematical equations for the constitutive model are summarized below -\f{align*} - \mathbf{M} &= \mathbf{\sigma}, \\ - \bar{\sigma} &= \frac{3}{2} \lVert \operatorname{dev}(\mathbf{M}) \rVert, \\ - k &= H \bar{\varepsilon}^p, \\ - f^p &= \bar{\sigma} - \sigma_y - k, \\ - \mathbf{N}_M &= \partial f^p / \partial \mathbf{M}, \\ - N_k &= \partial f^p / \partial k, \\ - \dot{\gamma} &= \left( \frac{\abs{f^p}}{\eta} \right)^n \operatorname{heav}\left( f^p \right), \\ - \dot{\mathbf{\varepsilon}}^p &= \dot{\gamma} \mathbf{N}_M, \\ - \dot{\bar{\varepsilon}}^p &= \dot{\gamma} N_k, \\ - \dot{\mathbf{\varepsilon}} &= \frac{\mathbf{\varepsilon} - \mathbf{\varepsilon}_n}{t - t_n}, \\ - \dot{\mathbf{\varepsilon}}^e &= \dot{\mathbf{\varepsilon}} - \dot{\mathbf{\varepsilon}}^p, \\ - \dot{\mathbf{\sigma}} &= K \operatorname{vol}\left( \dot{\mathbf{\varepsilon}}^e \right) + G\operatorname{dev}\left( \dot{\mathbf{\varepsilon}}^e \right), \\ - \mathbf{r} &= - \begin{Bmatrix} - \mathbf{\sigma} - \mathbf{\sigma}_n - \dot{\mathbf{\sigma}} (t - t_n) \\ - \bar{\varepsilon}^p - \bar{\varepsilon}^p_n - \dot{\bar{\varepsilon}}^p (t - t_n) - \end{Bmatrix}, \\ - \left( \mathbf{\sigma}, \bar{\varepsilon}^p \right) &= \operatorname{sol}\left( \mathbf{r} = \mathbf{0} \right). -\f} -where \f$\mathbf{\sigma}\f$ is the Cauchy stress, \f$\mathbf{M}\f$ is the Mandel stress, \f$\bar{\sigma}\f$ is the effective stress, \f$\bar{\varepsilon}^p\f$ is the equivalent plastic strain, \f$k\f$ is the isotropic hardening, \f$f^p\f$ is the plastic yield function, \f$\mathbf{N}_M\f$ is the plastic flow direction, \f$N_k\f$ is the isotropic hardening direction, \f$\dot{\gamma}\f$ is the plastic flow rate, \f$\mathbf{\varepsilon}^p\f$ is the plastic strain, \f$\mathbf{\varepsilon}\f$ is the strain, \f$t\f$ is the time, and \f$\mathbf{\varepsilon}^e\f$ is the elastic strain. - -Each of the equation above is a function mapping some input variables to some output variables, hence can be defined as a NEML2 model. The example constitutive model under consideration is the composition of these models. Recall that the input variables of a composed model is the set of root input variables, and the output variables of a composed model is the set of leaf output variables. By inspection, the input variables for this composed model are -\f[ - \mathbf{\varepsilon}, t, \mathbf{\varepsilon}_n, t_n, \mathbf{\sigma}_n, \bar{\varepsilon}^p_n, -\f] -and the output variables for this composed model are -\f[ - \mathbf{\sigma}, \bar{\varepsilon}^p. -\f] - -Finally, a constitutive model is completely defined up to a set of _parameters_. In this example, the parameters of the composed model under consideration are -\f[ - H, \sigma_y, \eta, n, K, G. -\f] - -## Naming conventions - -Recall that NEML2 models operates on _labeled tensors_, and that the collection of labels (with their corresponding layout) is called an labeled axis ([LabeledAxis](@ref neml2::LabeledAxis)). NEML2 predefines 6 sub-axes to categorize all the input, output and intermediate variables: -- State \f$\mathcal{S}\f$: Variables on the "state" sub-axis collectively characterize the current _state_ of the material subject to given external forces. The state variables are usually the output of a physically meaningful constitutive model. -- Forces \f$\mathcal{F}\f$: Variables on the "forces" sub-axis define the _external_ forces that drive the response of the material. -- Old state \f$\mathcal{S}_n\f$: The state variables _prior to_ the current constitutive update. In the time-discrete setting, these are the state variables from the previous time step. -- Old forces \f$\mathcal{F}_n\f$: The external forces _prior to_ the current constitutive update. In the time-discrete setting, these are the forces from the previous time step. -- Residual \f$\mathcal{R}\f$: The residual defines an _implicit_ model/function. An implicit model is updated by solving for the state variables that result in zero residual. -- Trial state \f$\tilde{\mathcal{S}}\f$: The state variables during an implicit update that result in a nonzero residual. - -In NEML2, the following naming conventions are recommended: -- User-facing variables and parameters should be _as descriptive as possible_. For example, the equivalent plastic strain is named "equivalent_plastic_strain". Note that white spaces, quotes, and left slashes are not allowed in the names. Underscores are recommended as an replacement for white spaces. -- Developer-facing variables and parameters should use simple alphanumeric symbols. For example, the equivalent plastic strain is named "ep" in consistency with most of the existing literature. -- Developner-facing member variables and parameters should use the same alphanumeric symbols. For example, the member variable for the equivalent plastic strain is named `ep`. However, if the member variable is protected or private, it is recommended to prefix it with an underscore, i.e. `_ep`. -- Struct names and class names should use `PascalCase`. -- Function names should use `snake_case`. - -## Tutorial 1: Variable declaration and registration - -The development of every model begins with the declaration and registration of its input and output variables. Here, we first define an abstract base class that will be later used to define the linear isotropic hardening relation. The abstract base class defines the isotropic hardening relation -\f[ - k = f\left( \bar{\varepsilon}^p \right), -\f] -mapping the equivalent plastic strain to the isotropic hardening. Recall that NEML2 emphasizes modularity, and so the definition of this model _does not_ concern any other model in the final composition. The base class is named `IsotropicHardening` following the naming convention. The header file [IsotropicHardening.h](@ref neml2::IsotropicHardening) is displayed below -```cpp -#pragma once - -#include "neml2/models/Model.h" - -namespace neml2 -{ -class IsotropicHardening : public Model -{ -public: - static OptionSet expected_options(); - - IsotropicHardening(const OptionSet & options); - - const VariableName equivalent_plastic_strain; - const VariableName isotropic_hardening; -}; -} // namespace neml2 -``` -Since isotropic hardening _is a_ model, the class inherits from `Model`. The user-facing expected parameters are defined by the static method `expected_options`. NEML2 handles the parsing of user parameters and pass them to the constructor. The input variable of the model is the equivalent plastic strain, and the output variable of the model is the isotropic hardening. Their corresponding variable accessors are stored as public member variables `equivalent_plastic_strain` and `isotropic_hardening`, respectively. - -The expected parameters and the constructor are defined as -```cpp -#include "neml2/models/solid_mechanics/IsotropicHardening.h" - -namespace neml2 -{ -OptionSet -IsotropicHardening::expected_options() -{ - OptionSet options = Model::expected_options(); - options.set("equivalent_plastic_strain") = {{"state", "internal", "ep"}}; - options.set("isotropic_hardening") = {{"state", "internal", "k"}}; - return options; -} - -IsotropicHardening::IsotropicHardening(const OptionSet & options) - : Model(options), - equivalent_plastic_strain(declare_input_variable( - options.get("equivalent_plastic_strain"))), - isotropic_hardening( - declare_output_variable(options.get("isotropic_hardening"))) -{ - setup(); -} -} // namespace neml2 -``` -Both the equivalent plastic strain and the isotropic hardening are internal state, denoted as "state/internal/ep" and "state/internal/k", respectively. The `IsotropicHardening` is constructed by extracting user-specified parameters. Note how `declare_input_variable` and `declare_output_variable` are used to declare and register the input and output variables. - -## Tutorial 2: Parameter declaration and registraion - -Now that the abstract base class `IsotropicHardening` has been implemented, we are ready to define our first concrete NEML2 model that describes a linear isotropic hardening relation -\f[ - k = H \bar{\varepsilon}^p. -\f] -Note that \f$H\f$ is a model parameter. Following the naming convention, the concrete class is named `LinearIsotropicHardening`. The header file is displayed below. -```cpp -#pragma once - -#include "neml2/models/solid_mechanics/IsotropicHardening.h" - -namespace neml2 -{ -class LinearIsotropicHardening : public IsotropicHardening -{ -public: - static OptionSet expected_options(); - - LinearIsotropicHardening(const OptionSet & options); - -protected: - /// Simple linear map between equivalent strain and hardening - void set_value(const LabeledVector & in, - LabeledVector * out, - LabeledMatrix * dout_din = nullptr, - LabeledTensor3D * d2out_din2 = nullptr) const override; - - Scalar _K; -}; -} // namespace neml2 -``` -It derives from the abstract base class `IsotropicHardening` and implements the virtual method `set_value` as the forward operator. The model parameter \f$H\f$ is stored as a protected member variable `_K`. The model implementation is shown below. -```cpp -#include "neml2/models/solid_mechanics/LinearIsotropicHardening.h" - -namespace neml2 -{ -register_NEML2_object(LinearIsotropicHardening); - -OptionSet -LinearIsotropicHardening::expected_options() -{ - OptionSet options = IsotropicHardening::expected_options(); - options.set>("hardening_modulus"); - return options; -} - -LinearIsotropicHardening::LinearIsotropicHardening(const OptionSet & options) - : IsotropicHardening(options), - _K(declare_parameter("K", "hardening_modulus")) -{ -} - -void -LinearIsotropicHardening::set_value(const LabeledVector & in, - LabeledVector * out, - LabeledMatrix * dout_din, - LabeledTensor3D * d2out_din2) const -{ - if (out) - out->set(_K * in(equivalent_plastic_strain), isotropic_hardening); - - if (dout_din) - dout_din->set(_K, isotropic_hardening, equivalent_plastic_strain); - - if (d2out_din2) - { - // zero - } -} -} // namespace neml2 -``` -Note that an additional option named "hardening_modulus" is requested from the user. The model parameter is registered using the API `declare_parameter`. In the `set_value` method, the current value of the input variable equivalent plastic strain is queried by -```cpp -in(equivalent_plastic_strain) -``` -and the isotropic hardening is computed as -```cpp -_K * in(equivalent_plastic_strain) -``` -The computed result is copied into the model output `out` by -```cpp -out->set(_K * in(equivalent_plastic_strain), isotropic_hardening); -``` -In addition, the first derivative of the forward operator is defined as -```cpp -dout_din->set(_K, isotropic_hardening, equivalent_plastic_strain); -``` -Finally, the model is registed in the NEML2 model factory using the macro -```cpp -register_NEML2_object(LinearIsotropicHardening); -``` - -## Tutorial 3: Testing - -It is of paramount importance to ensure the correctness of the implementation. NEML2 offers 5 types of tests with different purposes: - -### Catch2 test - -A Catch2 test refers to a test directly written in C++ source code within the Catch2 framework. It offers the highest level of flexibility, but requires more effort to set up. To understand how a Catch2 test works, please refer to the [official Catch2 documentation](https://github.com/catchorg/Catch2/blob/v2.x/docs/tutorial.md). - -### Model unit test - -A model unit test examines the outputs of a `Model` given a predefined set of inputs. Model unit tests can be directly designed using the input file syntax with the `ModelUnitTest` type. A variety of checks can be turned on and off. To list a few: `check_first_derivatives` compares the implemented first order derivatives of the model against finite-differencing results, and passes only if the two derivatives are within tolerances specified with `derivative_abs_tol` and `derivative_rel_tol`; `check_cuda` repeats all checks twice: once on CPU and once on GPU (if available), and passes only if the two evaluations yield same results within tolerances. - -All input files for model unit tests should be stored inside `tests/unit/models`. Every input file with the `.i` extension will be automatically discovered and executed. To run all the model unit tests, use the following commands -``` -cd tests -./unit_tests models -``` - -To run a specific model unit test, use the `-c` command line option followed by the relative location of the input file, i.e. -``` -cd tests -./unit_tests models -c solid_mechanics/LinearIsotropicElasticity.i -``` - -### Model regression test - -A model regression test runs a `Model` using a user specified driver. The results are compared against a predefined reference (stored on the disk inside the repository). The test passes only if the current results are the same as the predefined reference (again within specified tolerances). The regression tests ensure the consistency of implementations across commits. Currently, `TransientRegression` is the only supported type of regression test. - -Each input file for model regression tests should be stored inside a separate folder inside `tests/regression`. Every input file with the `.i` extension will be automatically discovered and executed. To run all the model regression tests, use the `regression_tests` executable followed by the physics module, i.e. -``` -cd tests -./regression_tests "solid mechanics" -``` -To run a specific model regression test, use the `-c` command line option followed by the relative location of the input file, i.e. -``` -cd tests -./regression_tests "solid mechanics" -c viscoplasticity/chaboche/model.i -``` -Note that the regression test expectes a option `reference` which specifies the relative location to the reference solution. - -### Model verification test - -The model verification test is similar to the model regression test in terms of workflow. The difference is the a verification test defines the reference solution using NEML, the predecessor of NEML2. Since NEML was developed with strict software assurance, the verification tests ensure that the migration from NEML to NEML2 is as smooth as it can be. - -Each input file for model verification tests should be stored inside a separate folder inside `tests/verification`. Every input file with the `.i` extension will be automatically discovered and executed. To run all the model verification tests, use the `verification_tests` executable followed by the physics module, i.e. -``` -cd tests -./verification_tests "solid mechanics" -``` - -To run a specific model verification test, use the `-c` command line option followed by the relative location of the input file, i.e. -``` -cd tests -./verification_tests "solid mechanics" -c chaboche/chaboche.i -``` -The regression test compares variables (specified using the `variables` option) against reference values (specified using the `references` option). The reference variables can be read using input objects with type `VTestTimeSeries`. - -### Benchmarking - -The benchmark tests can be authored within the [Catch2 microbenchmarking framework](https://github.com/catchorg/Catch2/blob/v2.x/docs/benchmarks.md). Before any benchmarks can be executed, the clock's resolution is estimated. A few other environmental artifacts are also estimated at this point, like the cost of calling the clock function, but they almost never have any impact in the results. The user code is executed a few times to obtain an estimate of the amount of runs that should be in each sample. This also has the potential effect of bringing relevant code and data into the caches before the actual measurement starts. Finally, all the samples are collected sequentially by performing the number of runs estimated in the previous step for each sample. - -To run a benchmark test, use the `benchmark.sh` script inside the `scripts` directory with 3 positional arguments: -``` -./scripts/benchmark.sh Chaboche 5 timings -``` -The first positional argument specifies the name of the benchmark test to run. The second positional argument specifies the number of samples to repeat in each iteration. The third positional argument specifies the output directory of the benchmark results. - -The Chaboche benchmark test is repeated with different batch sizes and on different devices (in this case CPU and GPU). The final benchmark results are summarized in the following figure. - -![Chaboche benchmark results](@ref timings.png){html: width=50%, latex: width=10cm} diff --git a/doc/content/_04_system.md b/doc/content/_04_system.md new file mode 100644 index 0000000000..4ea7af8b57 --- /dev/null +++ b/doc/content/_04_system.md @@ -0,0 +1,3 @@ +# System Documentation {#system} + +[TOC] diff --git a/src/neml2/CMakeLists.txt b/src/neml2/CMakeLists.txt index 73f199dfc8..4ed7b52f39 100644 --- a/src/neml2/CMakeLists.txt +++ b/src/neml2/CMakeLists.txt @@ -7,8 +7,7 @@ add_library(neml2 SHARED ${SRCS}) # Make scalar type configurable: if(NOT NEML2_DTYPE) set(NEML2_DTYPE "Float64" CACHE STRING "Default NEML2 scalar type." FORCE) - set_property(CACHE NEML2_DTYPE PROPERTY STRINGS - "UInt8" "Int8" "Int16" "Int32" "Int64" "Float16" "Float32" "Float64") + set_property(CACHE NEML2_DTYPE PROPERTY STRINGS "Float16" "Float32" "Float64") endif() message(STATUS "Configuring with default scalar type: ${NEML2_DTYPE}") @@ -16,8 +15,7 @@ message(STATUS "Configuring with default scalar type: ${NEML2_DTYPE}") # Also want to configure an int type for specialized int tensors if(NOT NEML2_INT_DTYPE) set(NEML2_INT_DTYPE "Int64" CACHE STRING "Default NEML2 integer scalar type." FORCE) - set_property(CACHE NEML2_INT_DTYPE PROPERTY STRINGS - "Int8" "Int16" "Int32" "Int64") + set_property(CACHE NEML2_INT_DTYPE PROPERTY STRINGS "Int8" "Int16" "Int32" "Int64") endif() message(STATUS "Configuring with default integer scalar type: ${NEML2_INT_DTYPE}") From 73497e2f6b8a51414c21d6ec6b8d8b69126eb00b Mon Sep 17 00:00:00 2001 From: Gary Hu Date: Thu, 2 May 2024 17:17:04 -0500 Subject: [PATCH 04/55] Improve syntax documentation --- doc/CMakeLists.txt | 2 +- doc/syntax.cxx | 11 ++- include/neml2/base/OptionSet.h | 14 +++- include/neml2/base/Registry.h | 9 ++- .../user_tensors/FillMillerIndex.h | 5 +- .../user_tensors/SymmetryFromOrbifold.h | 7 +- .../tensors/user_tensors/EmptyBatchTensor.h | 5 +- .../user_tensors/EmptyFixedDimTensor.h | 5 +- .../neml2/tensors/user_tensors/Fill3DVec.h | 5 +- include/neml2/tensors/user_tensors/FillR2.h | 5 +- include/neml2/tensors/user_tensors/FillRot.h | 5 +- include/neml2/tensors/user_tensors/FillSR2.h | 5 +- include/neml2/tensors/user_tensors/FillWR2.h | 5 +- .../tensors/user_tensors/FullBatchTensor.h | 5 +- .../tensors/user_tensors/FullFixedDimTensor.h | 5 +- .../user_tensors/IdentityBatchTensor.h | 5 +- .../user_tensors/LinspaceBatchTensor.h | 5 +- .../user_tensors/LinspaceFixedDimTensor.h | 5 +- .../user_tensors/LogspaceBatchTensor.h | 5 +- .../user_tensors/LogspaceFixedDimTensor.h | 5 +- .../tensors/user_tensors/OnesBatchTensor.h | 5 +- .../tensors/user_tensors/OnesFixedDimTensor.h | 5 +- .../neml2/tensors/user_tensors/Orientation.h | 5 +- .../tensors/user_tensors/UserBatchTensor.h | 5 +- .../tensors/user_tensors/UserFixedDimTensor.h | 5 +- .../neml2/tensors/user_tensors/UserTensor.h | 39 +++++++++++ .../tensors/user_tensors/ZerosBatchTensor.h | 5 +- .../user_tensors/ZerosFixedDimTensor.h | 5 +- scripts/syntax_to_md.py | 67 +++++++++++-------- src/neml2/base/NEML2Object.cxx | 1 + src/neml2/base/OptionSet.cxx | 11 ++- src/neml2/base/Registry.cxx | 28 ++++---- src/neml2/drivers/Driver.cxx | 1 + src/neml2/models/ArrheniusParameter.cxx | 13 ++++ src/neml2/models/Data.cxx | 1 + src/neml2/models/Model.cxx | 7 ++ .../user_tensors/FillMillerIndex.cxx | 4 +- .../user_tensors/SymmetryFromOrbifold.cxx | 6 +- src/neml2/solvers/Solver.cxx | 1 + .../tensors/user_tensors/EmptyBatchTensor.cxx | 4 +- .../user_tensors/EmptyFixedDimTensor.cxx | 4 +- src/neml2/tensors/user_tensors/Fill3DVec.cxx | 4 +- src/neml2/tensors/user_tensors/FillR2.cxx | 4 +- src/neml2/tensors/user_tensors/FillRot.cxx | 4 +- src/neml2/tensors/user_tensors/FillSR2.cxx | 4 +- src/neml2/tensors/user_tensors/FillWR2.cxx | 4 +- .../tensors/user_tensors/FullBatchTensor.cxx | 4 +- .../user_tensors/FullFixedDimTensor.cxx | 4 +- .../user_tensors/IdentityBatchTensor.cxx | 4 +- .../user_tensors/LinspaceBatchTensor.cxx | 4 +- .../user_tensors/LinspaceFixedDimTensor.cxx | 4 +- .../user_tensors/LogspaceBatchTensor.cxx | 4 +- .../user_tensors/LogspaceFixedDimTensor.cxx | 4 +- .../tensors/user_tensors/OnesBatchTensor.cxx | 4 +- .../user_tensors/OnesFixedDimTensor.cxx | 4 +- .../tensors/user_tensors/Orientation.cxx | 4 +- .../tensors/user_tensors/UserBatchTensor.cxx | 4 +- .../user_tensors/UserFixedDimTensor.cxx | 4 +- src/neml2/tensors/user_tensors/UserTensor.cxx | 41 ++++++++++++ .../tensors/user_tensors/ZerosBatchTensor.cxx | 4 +- .../user_tensors/ZerosFixedDimTensor.cxx | 4 +- 61 files changed, 289 insertions(+), 168 deletions(-) create mode 100644 include/neml2/tensors/user_tensors/UserTensor.h create mode 100644 src/neml2/tensors/user_tensors/UserTensor.cxx diff --git a/doc/CMakeLists.txt b/doc/CMakeLists.txt index 4467ddae62..6fc380ab4e 100644 --- a/doc/CMakeLists.txt +++ b/doc/CMakeLists.txt @@ -54,7 +54,7 @@ add_custom_target(doc-syntax DEPENDS syntax WORKING_DIRECTORY ${NEML2_BINARY_DIR}/doc COMMAND ${NEML2_BINARY_DIR}/doc/syntax - COMMAND ${Python_EXECUTABLE} ${NEML2_SOURCE_DIR}/scripts/syntax_to_md.py syntax.txt content/_99_syntax.md + COMMAND ${Python_EXECUTABLE} ${NEML2_SOURCE_DIR}/scripts/syntax_to_md.py syntax.yml content/_99_syntax.md COMMENT "Generate NEML2 syntax and convert to markdown" VERBATIM ) diff --git a/doc/syntax.cxx b/doc/syntax.cxx index 45f23d9564..e3675e92d2 100644 --- a/doc/syntax.cxx +++ b/doc/syntax.cxx @@ -29,8 +29,15 @@ int main() { std::ofstream syntax_file; - syntax_file.open("syntax.txt"); - neml2::Registry::print(syntax_file); + syntax_file.open("syntax.yml"); + + for (auto && [type, options] : neml2::Registry::expected_options()) + { + options.set("type") = type; + syntax_file << neml2::Registry::syntax_type(type) << ":\n"; + syntax_file << options << "\n"; + } + syntax_file.close(); return 0; } diff --git a/include/neml2/base/OptionSet.h b/include/neml2/base/OptionSet.h index 69ca03a591..0f9fc719d9 100644 --- a/include/neml2/base/OptionSet.h +++ b/include/neml2/base/OptionSet.h @@ -91,6 +91,10 @@ class OptionSet const std::string & doc() const { return _metadata.doc; } /// A writable reference to the option set's docstring std::string & doc() { return _metadata.doc; } + /// A readonly reference to the option set's section + const std::string & section() const { return _metadata.section; } + /// A writable reference to the option set's section + std::string & section() { return _metadata.section; } /** * \returns \p true if an option of type \p T @@ -200,7 +204,7 @@ class OptionSet * object to suppress certain option. A suppressed option cannot be modified by the user. It * is up to the specific Parser to decide what happens when a user attempts to set a * suppressed option, e.g., the parser can choose to throw an exception, print a warning and - * accepts it, or print a warning and ignores it. + * accept it, or print a warning and ignores it. */ bool suppressed = false; } _metadata; @@ -345,6 +349,14 @@ class OptionSet * https://www.doxygen.nl/manual/markdown.html */ std::string doc = ""; + /** + * @brief Which NEML2 input file section this object belongs to + * + * NEML2 supports first class systems such as [Tensors], [Models], [Drivers], [Solvers], etc. + * This field denotes which section, i.e. which of the first class system, this object belongs + * to. + */ + std::string section = ""; } _metadata; /// Data structure to map names with values diff --git a/include/neml2/base/Registry.h b/include/neml2/base/Registry.h index 601d7a20f3..350c89621e 100644 --- a/include/neml2/base/Registry.h +++ b/include/neml2/base/Registry.h @@ -62,15 +62,18 @@ class Registry return 0; } + /// Return the expected options of all registered classs + static std::map expected_options(); + /// Return the expected options of a specific registered class static OptionSet expected_options(const std::string & name); + /// Return the syntax type (what appears in the input file) given a registered object's type + static std::string syntax_type(const std::string & type); + /// Return the build method pointer of a specific registered class static BuildPtr builder(const std::string & name); - /// List all registered objects - static void print(std::ostream & os = std::cout); - private: Registry() {} diff --git a/include/neml2/models/crystallography/user_tensors/FillMillerIndex.h b/include/neml2/models/crystallography/user_tensors/FillMillerIndex.h index c1bde07609..c6b93ae495 100644 --- a/include/neml2/models/crystallography/user_tensors/FillMillerIndex.h +++ b/include/neml2/models/crystallography/user_tensors/FillMillerIndex.h @@ -24,8 +24,7 @@ #pragma once -#include "neml2/base/Registry.h" -#include "neml2/base/NEML2Object.h" +#include "neml2/tensors/user_tensors/UserTensor.h" #include "neml2/models/crystallography/MillerIndex.h" @@ -36,7 +35,7 @@ namespace crystallography /** * @brief Create a single-batched "list" of Miller indices */ -class FillMillerIndex : public MillerIndex, public NEML2Object +class FillMillerIndex : public MillerIndex, public UserTensor { public: static OptionSet expected_options(); diff --git a/include/neml2/models/crystallography/user_tensors/SymmetryFromOrbifold.h b/include/neml2/models/crystallography/user_tensors/SymmetryFromOrbifold.h index a0a0639505..dd961211cf 100644 --- a/include/neml2/models/crystallography/user_tensors/SymmetryFromOrbifold.h +++ b/include/neml2/models/crystallography/user_tensors/SymmetryFromOrbifold.h @@ -24,8 +24,7 @@ #pragma once -#include "neml2/base/Registry.h" -#include "neml2/base/NEML2Object.h" +#include "neml2/tensors/user_tensors/UserTensor.h" #include "neml2/tensors/R2.h" @@ -36,7 +35,7 @@ namespace crystallography /** * @brief Provide the correct symmetry operators for a given crystal class */ -class SymmetryFromOrbifold : public R2, public NEML2Object +class SymmetryFromOrbifold : public R2, public UserTensor { public: static OptionSet expected_options(); @@ -49,4 +48,4 @@ class SymmetryFromOrbifold : public R2, public NEML2Object SymmetryFromOrbifold(const OptionSet & options); }; } // namespace crystallography -} // namespace neml2 \ No newline at end of file +} // namespace neml2 diff --git a/include/neml2/tensors/user_tensors/EmptyBatchTensor.h b/include/neml2/tensors/user_tensors/EmptyBatchTensor.h index 379fa36e72..699ec1501d 100644 --- a/include/neml2/tensors/user_tensors/EmptyBatchTensor.h +++ b/include/neml2/tensors/user_tensors/EmptyBatchTensor.h @@ -24,8 +24,7 @@ #pragma once -#include "neml2/base/Registry.h" -#include "neml2/base/NEML2Object.h" +#include "neml2/tensors/user_tensors/UserTensor.h" #include "neml2/tensors/BatchTensor.h" @@ -34,7 +33,7 @@ namespace neml2 /** * @brief Create an empty BatchTensor from the input file. */ -class EmptyBatchTensor : public BatchTensor, public NEML2Object +class EmptyBatchTensor : public BatchTensor, public UserTensor { public: static OptionSet expected_options(); diff --git a/include/neml2/tensors/user_tensors/EmptyFixedDimTensor.h b/include/neml2/tensors/user_tensors/EmptyFixedDimTensor.h index 11ce394e57..d84504ed57 100644 --- a/include/neml2/tensors/user_tensors/EmptyFixedDimTensor.h +++ b/include/neml2/tensors/user_tensors/EmptyFixedDimTensor.h @@ -24,8 +24,7 @@ #pragma once -#include "neml2/base/Registry.h" -#include "neml2/base/NEML2Object.h" +#include "neml2/tensors/user_tensors/UserTensor.h" #include "neml2/tensors/macros.h" #include "neml2/tensors/tensors.h" @@ -38,7 +37,7 @@ namespace neml2 * @tparam T The concrete tensor derived from FixedDimTensor */ template -class EmptyFixedDimTensor : public T, public NEML2Object +class EmptyFixedDimTensor : public T, public UserTensor { public: static OptionSet expected_options(); diff --git a/include/neml2/tensors/user_tensors/Fill3DVec.h b/include/neml2/tensors/user_tensors/Fill3DVec.h index baf5cb2e08..60e37c2a1a 100644 --- a/include/neml2/tensors/user_tensors/Fill3DVec.h +++ b/include/neml2/tensors/user_tensors/Fill3DVec.h @@ -24,8 +24,7 @@ #pragma once -#include "neml2/base/Registry.h" -#include "neml2/base/NEML2Object.h" +#include "neml2/tensors/user_tensors/UserTensor.h" #include "neml2/tensors/Vec.h" @@ -34,7 +33,7 @@ namespace neml2 /** * @brief Create a single-batched "list" of 3D vectors */ -class Fill3DVec : public Vec, public NEML2Object +class Fill3DVec : public Vec, public UserTensor { public: static OptionSet expected_options(); diff --git a/include/neml2/tensors/user_tensors/FillR2.h b/include/neml2/tensors/user_tensors/FillR2.h index bcd141b761..d1ab63992a 100644 --- a/include/neml2/tensors/user_tensors/FillR2.h +++ b/include/neml2/tensors/user_tensors/FillR2.h @@ -24,8 +24,7 @@ #pragma once -#include "neml2/base/Registry.h" -#include "neml2/base/NEML2Object.h" +#include "neml2/tensors/user_tensors/UserTensor.h" #include "neml2/tensors/R2.h" @@ -34,7 +33,7 @@ namespace neml2 /** * @brief Create a filled R2 from the input file. */ -class FillR2 : public R2, public NEML2Object +class FillR2 : public R2, public UserTensor { public: static OptionSet expected_options(); diff --git a/include/neml2/tensors/user_tensors/FillRot.h b/include/neml2/tensors/user_tensors/FillRot.h index 82616bc887..f05dbd84f2 100644 --- a/include/neml2/tensors/user_tensors/FillRot.h +++ b/include/neml2/tensors/user_tensors/FillRot.h @@ -24,8 +24,7 @@ #pragma once -#include "neml2/base/Registry.h" -#include "neml2/base/NEML2Object.h" +#include "neml2/tensors/user_tensors/UserTensor.h" #include "neml2/tensors/Rot.h" @@ -34,7 +33,7 @@ namespace neml2 /** * @brief Create a filled R2 from the input file. */ -class FillRot : public Rot, public NEML2Object +class FillRot : public Rot, public UserTensor { public: static OptionSet expected_options(); diff --git a/include/neml2/tensors/user_tensors/FillSR2.h b/include/neml2/tensors/user_tensors/FillSR2.h index d8bb9cdff4..48fd6b94b1 100644 --- a/include/neml2/tensors/user_tensors/FillSR2.h +++ b/include/neml2/tensors/user_tensors/FillSR2.h @@ -24,8 +24,7 @@ #pragma once -#include "neml2/base/Registry.h" -#include "neml2/base/NEML2Object.h" +#include "neml2/tensors/user_tensors/UserTensor.h" #include "neml2/tensors/SR2.h" @@ -34,7 +33,7 @@ namespace neml2 /** * @brief Create a filled SR2 from the input file. */ -class FillSR2 : public SR2, public NEML2Object +class FillSR2 : public SR2, public UserTensor { public: static OptionSet expected_options(); diff --git a/include/neml2/tensors/user_tensors/FillWR2.h b/include/neml2/tensors/user_tensors/FillWR2.h index 10164763b9..8486fef68d 100644 --- a/include/neml2/tensors/user_tensors/FillWR2.h +++ b/include/neml2/tensors/user_tensors/FillWR2.h @@ -24,8 +24,7 @@ #pragma once -#include "neml2/base/Registry.h" -#include "neml2/base/NEML2Object.h" +#include "neml2/tensors/user_tensors/UserTensor.h" #include "neml2/tensors/WR2.h" @@ -34,7 +33,7 @@ namespace neml2 /** * @brief Create a filled WR2 from the input file. */ -class FillWR2 : public WR2, public NEML2Object +class FillWR2 : public WR2, public UserTensor { public: static OptionSet expected_options(); diff --git a/include/neml2/tensors/user_tensors/FullBatchTensor.h b/include/neml2/tensors/user_tensors/FullBatchTensor.h index 5e71a11b5a..9c0e446382 100644 --- a/include/neml2/tensors/user_tensors/FullBatchTensor.h +++ b/include/neml2/tensors/user_tensors/FullBatchTensor.h @@ -24,8 +24,7 @@ #pragma once -#include "neml2/base/Registry.h" -#include "neml2/base/NEML2Object.h" +#include "neml2/tensors/user_tensors/UserTensor.h" #include "neml2/tensors/BatchTensor.h" @@ -34,7 +33,7 @@ namespace neml2 /** * @brief Create a full BatchTensor from the input file. */ -class FullBatchTensor : public BatchTensor, public NEML2Object +class FullBatchTensor : public BatchTensor, public UserTensor { public: static OptionSet expected_options(); diff --git a/include/neml2/tensors/user_tensors/FullFixedDimTensor.h b/include/neml2/tensors/user_tensors/FullFixedDimTensor.h index 7506a2f757..1520bc786f 100644 --- a/include/neml2/tensors/user_tensors/FullFixedDimTensor.h +++ b/include/neml2/tensors/user_tensors/FullFixedDimTensor.h @@ -24,8 +24,7 @@ #pragma once -#include "neml2/base/Registry.h" -#include "neml2/base/NEML2Object.h" +#include "neml2/tensors/user_tensors/UserTensor.h" #include "neml2/tensors/macros.h" #include "neml2/tensors/tensors.h" @@ -38,7 +37,7 @@ namespace neml2 * @tparam T The concrete tensor derived from FixedDimTensor */ template -class FullFixedDimTensor : public T, public NEML2Object +class FullFixedDimTensor : public T, public UserTensor { public: static OptionSet expected_options(); diff --git a/include/neml2/tensors/user_tensors/IdentityBatchTensor.h b/include/neml2/tensors/user_tensors/IdentityBatchTensor.h index d089b96c2a..1b47b2b9b9 100644 --- a/include/neml2/tensors/user_tensors/IdentityBatchTensor.h +++ b/include/neml2/tensors/user_tensors/IdentityBatchTensor.h @@ -24,8 +24,7 @@ #pragma once -#include "neml2/base/Registry.h" -#include "neml2/base/NEML2Object.h" +#include "neml2/tensors/user_tensors/UserTensor.h" #include "neml2/tensors/BatchTensor.h" @@ -34,7 +33,7 @@ namespace neml2 /** * @brief Create an identity BatchTensor from the input file. */ -class IdentityBatchTensor : public BatchTensor, public NEML2Object +class IdentityBatchTensor : public BatchTensor, public UserTensor { public: static OptionSet expected_options(); diff --git a/include/neml2/tensors/user_tensors/LinspaceBatchTensor.h b/include/neml2/tensors/user_tensors/LinspaceBatchTensor.h index 0e7d85c86b..5e1cdd8d7e 100644 --- a/include/neml2/tensors/user_tensors/LinspaceBatchTensor.h +++ b/include/neml2/tensors/user_tensors/LinspaceBatchTensor.h @@ -24,8 +24,7 @@ #pragma once -#include "neml2/base/Registry.h" -#include "neml2/base/NEML2Object.h" +#include "neml2/tensors/user_tensors/UserTensor.h" #include "neml2/tensors/BatchTensor.h" @@ -34,7 +33,7 @@ namespace neml2 /** * @brief Create a linspace BatchTensor from the input file. */ -class LinspaceBatchTensor : public BatchTensor, public NEML2Object +class LinspaceBatchTensor : public BatchTensor, public UserTensor { public: static OptionSet expected_options(); diff --git a/include/neml2/tensors/user_tensors/LinspaceFixedDimTensor.h b/include/neml2/tensors/user_tensors/LinspaceFixedDimTensor.h index 2ea8c3b701..0bd980ff5c 100644 --- a/include/neml2/tensors/user_tensors/LinspaceFixedDimTensor.h +++ b/include/neml2/tensors/user_tensors/LinspaceFixedDimTensor.h @@ -24,8 +24,7 @@ #pragma once -#include "neml2/base/Registry.h" -#include "neml2/base/NEML2Object.h" +#include "neml2/tensors/user_tensors/UserTensor.h" #include "neml2/tensors/macros.h" #include "neml2/tensors/tensors.h" @@ -38,7 +37,7 @@ namespace neml2 * @tparam T The concrete tensor derived from FixedDimTensor */ template -class LinspaceFixedDimTensor : public T, public NEML2Object +class LinspaceFixedDimTensor : public T, public UserTensor { public: static OptionSet expected_options(); diff --git a/include/neml2/tensors/user_tensors/LogspaceBatchTensor.h b/include/neml2/tensors/user_tensors/LogspaceBatchTensor.h index 39f86c06fd..04bec52a94 100644 --- a/include/neml2/tensors/user_tensors/LogspaceBatchTensor.h +++ b/include/neml2/tensors/user_tensors/LogspaceBatchTensor.h @@ -24,8 +24,7 @@ #pragma once -#include "neml2/base/Registry.h" -#include "neml2/base/NEML2Object.h" +#include "neml2/tensors/user_tensors/UserTensor.h" #include "neml2/tensors/BatchTensor.h" @@ -34,7 +33,7 @@ namespace neml2 /** * @brief Create a logspace BatchTensor from the input file. */ -class LogspaceBatchTensor : public BatchTensor, public NEML2Object +class LogspaceBatchTensor : public BatchTensor, public UserTensor { public: static OptionSet expected_options(); diff --git a/include/neml2/tensors/user_tensors/LogspaceFixedDimTensor.h b/include/neml2/tensors/user_tensors/LogspaceFixedDimTensor.h index 4c17bc7572..0f7a16795f 100644 --- a/include/neml2/tensors/user_tensors/LogspaceFixedDimTensor.h +++ b/include/neml2/tensors/user_tensors/LogspaceFixedDimTensor.h @@ -24,8 +24,7 @@ #pragma once -#include "neml2/base/Registry.h" -#include "neml2/base/NEML2Object.h" +#include "neml2/tensors/user_tensors/UserTensor.h" #include "neml2/tensors/macros.h" #include "neml2/tensors/tensors.h" @@ -38,7 +37,7 @@ namespace neml2 * @tparam T The concrete tensor derived from FixedDimTensor */ template -class LogspaceFixedDimTensor : public T, public NEML2Object +class LogspaceFixedDimTensor : public T, public UserTensor { public: static OptionSet expected_options(); diff --git a/include/neml2/tensors/user_tensors/OnesBatchTensor.h b/include/neml2/tensors/user_tensors/OnesBatchTensor.h index 905d8f2bad..2e43c49220 100644 --- a/include/neml2/tensors/user_tensors/OnesBatchTensor.h +++ b/include/neml2/tensors/user_tensors/OnesBatchTensor.h @@ -24,8 +24,7 @@ #pragma once -#include "neml2/base/Registry.h" -#include "neml2/base/NEML2Object.h" +#include "neml2/tensors/user_tensors/UserTensor.h" #include "neml2/tensors/BatchTensor.h" @@ -34,7 +33,7 @@ namespace neml2 /** * @brief Create a ones BatchTensor from the input file. */ -class OnesBatchTensor : public BatchTensor, public NEML2Object +class OnesBatchTensor : public BatchTensor, public UserTensor { public: static OptionSet expected_options(); diff --git a/include/neml2/tensors/user_tensors/OnesFixedDimTensor.h b/include/neml2/tensors/user_tensors/OnesFixedDimTensor.h index 026a898afe..3c15f719f5 100644 --- a/include/neml2/tensors/user_tensors/OnesFixedDimTensor.h +++ b/include/neml2/tensors/user_tensors/OnesFixedDimTensor.h @@ -24,8 +24,7 @@ #pragma once -#include "neml2/base/Registry.h" -#include "neml2/base/NEML2Object.h" +#include "neml2/tensors/user_tensors/UserTensor.h" #include "neml2/tensors/macros.h" #include "neml2/tensors/tensors.h" @@ -38,7 +37,7 @@ namespace neml2 * @tparam T The concrete tensor derived from FixedDimTensor */ template -class OnesFixedDimTensor : public T, public NEML2Object +class OnesFixedDimTensor : public T, public UserTensor { public: static OptionSet expected_options(); diff --git a/include/neml2/tensors/user_tensors/Orientation.h b/include/neml2/tensors/user_tensors/Orientation.h index 5da65f77df..28359125f3 100644 --- a/include/neml2/tensors/user_tensors/Orientation.h +++ b/include/neml2/tensors/user_tensors/Orientation.h @@ -24,8 +24,7 @@ #pragma once -#include "neml2/base/Registry.h" -#include "neml2/base/NEML2Object.h" +#include "neml2/tensors/user_tensors/UserTensor.h" #include "neml2/tensors/Rot.h" @@ -34,7 +33,7 @@ namespace neml2 /** * @brief Create batch of rotations, with various methods */ -class Orientation : public Rot, public NEML2Object +class Orientation : public Rot, public UserTensor { public: static OptionSet expected_options(); diff --git a/include/neml2/tensors/user_tensors/UserBatchTensor.h b/include/neml2/tensors/user_tensors/UserBatchTensor.h index d386835777..99ce7782ce 100644 --- a/include/neml2/tensors/user_tensors/UserBatchTensor.h +++ b/include/neml2/tensors/user_tensors/UserBatchTensor.h @@ -24,8 +24,7 @@ #pragma once -#include "neml2/base/Registry.h" -#include "neml2/base/NEML2Object.h" +#include "neml2/tensors/user_tensors/UserTensor.h" #include "neml2/tensors/BatchTensor.h" @@ -34,7 +33,7 @@ namespace neml2 /** * @brief Create raw BatchTensor from the input file. */ -class UserBatchTensor : public BatchTensor, public NEML2Object +class UserBatchTensor : public BatchTensor, public UserTensor { public: static OptionSet expected_options(); diff --git a/include/neml2/tensors/user_tensors/UserFixedDimTensor.h b/include/neml2/tensors/user_tensors/UserFixedDimTensor.h index b4c5ed4441..7a57f66109 100644 --- a/include/neml2/tensors/user_tensors/UserFixedDimTensor.h +++ b/include/neml2/tensors/user_tensors/UserFixedDimTensor.h @@ -24,8 +24,7 @@ #pragma once -#include "neml2/base/Registry.h" -#include "neml2/base/NEML2Object.h" +#include "neml2/tensors/user_tensors/UserTensor.h" #include "neml2/tensors/macros.h" #include "neml2/tensors/tensors.h" @@ -38,7 +37,7 @@ namespace neml2 * @tparam T The concrete tensor derived from BatchTensorBase */ template -class UserFixedDimTensor : public T, public NEML2Object +class UserFixedDimTensor : public T, public UserTensor { public: static OptionSet expected_options(); diff --git a/include/neml2/tensors/user_tensors/UserTensor.h b/include/neml2/tensors/user_tensors/UserTensor.h new file mode 100644 index 0000000000..64053fa76c --- /dev/null +++ b/include/neml2/tensors/user_tensors/UserTensor.h @@ -0,0 +1,39 @@ +// Copyright 2023, UChicago Argonne, LLC +// All Rights Reserved +// Software Name: NEML2 -- the New Engineering material Model Library, version 2 +// By: Argonne National Laboratory +// OPEN SOURCE LICENSE (MIT) +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. + +#pragma once + +#include "neml2/base/Registry.h" +#include "neml2/base/NEML2Object.h" + +namespace neml2 +{ +class UserTensor : public NEML2Object +{ +public: + static OptionSet expected_options(); + + UserTensor(const OptionSet & options); +}; +} // namespace neml2 diff --git a/include/neml2/tensors/user_tensors/ZerosBatchTensor.h b/include/neml2/tensors/user_tensors/ZerosBatchTensor.h index c513404ccb..2a7797b2a9 100644 --- a/include/neml2/tensors/user_tensors/ZerosBatchTensor.h +++ b/include/neml2/tensors/user_tensors/ZerosBatchTensor.h @@ -24,8 +24,7 @@ #pragma once -#include "neml2/base/Registry.h" -#include "neml2/base/NEML2Object.h" +#include "neml2/tensors/user_tensors/UserTensor.h" #include "neml2/tensors/BatchTensor.h" @@ -34,7 +33,7 @@ namespace neml2 /** * @brief Create a zeros BatchTensor from the input file. */ -class ZerosBatchTensor : public BatchTensor, public NEML2Object +class ZerosBatchTensor : public BatchTensor, public UserTensor { public: static OptionSet expected_options(); diff --git a/include/neml2/tensors/user_tensors/ZerosFixedDimTensor.h b/include/neml2/tensors/user_tensors/ZerosFixedDimTensor.h index 52115f9156..a86ad9f066 100644 --- a/include/neml2/tensors/user_tensors/ZerosFixedDimTensor.h +++ b/include/neml2/tensors/user_tensors/ZerosFixedDimTensor.h @@ -24,8 +24,7 @@ #pragma once -#include "neml2/base/Registry.h" -#include "neml2/base/NEML2Object.h" +#include "neml2/tensors/user_tensors/UserTensor.h" #include "neml2/tensors/macros.h" #include "neml2/tensors/tensors.h" @@ -38,7 +37,7 @@ namespace neml2 * @tparam T The concrete tensor derived from FixedDimTensor */ template -class ZerosFixedDimTensor : public T, public NEML2Object +class ZerosFixedDimTensor : public T, public UserTensor { public: static OptionSet expected_options(); diff --git a/scripts/syntax_to_md.py b/scripts/syntax_to_md.py index e9450f4f28..f33c37d7e2 100755 --- a/scripts/syntax_to_md.py +++ b/scripts/syntax_to_md.py @@ -30,13 +30,6 @@ from pathlib import Path -def get_type(params): - for param in params: - for param_name, info in param.items(): - if param_name == "type": - return demangle(info["value"]) - - def demangle(type): type = type.replace( "std::__cxx11::basic_string, std::allocator >", @@ -52,10 +45,15 @@ def demangle(type): def postprocess(value, type): if type == "bool": - value = str(bool(value)) + value = "true" if value else "false" return value +def get_sections(syntax): + sections = [params["section"] for type, params in syntax.items()] + return set(sections) + + if __name__ == "__main__": outfile = Path(sys.argv[2]) outfile.parent.mkdir(parents=True, exist_ok=True) @@ -63,27 +61,42 @@ def postprocess(value, type): with open(sys.argv[1], "r") as stream: syntax = yaml.safe_load(stream) + sections = get_sections(syntax) + with open(sys.argv[2], "w") as stream: stream.write("# Syntax Documentation {#syntax}\n\n") stream.write("[TOC]\n\n") - for s in syntax: - for type, params in s.items(): - input_type = get_type(params) - stream.write("## {}\n\n".format(input_type)) - names = [] - types = [] - values = [] - for param in params: - for param_name, info in param.items(): - if param_name == "name": - continue - if param_name == "type": - continue - param_type = demangle(info["type"]) - param_value = postprocess(info["value"], param_type) - stream.write("- {}\n".format(param_name)) - stream.write(" - **Type**: {}\n".format(param_type)) - if param_value != None: - stream.write(" - **Default**: {}\n".format(param_value)) + + for section in sections: + stream.write("## [{}]\n\n".format(section)) + for type, params in syntax.items(): + if params["section"] != section: + continue + input_type = demangle(params["type"]["value"]) + stream.write( + "### {} {{#{}}}\n\n".format(input_type, input_type.lower()) + ) + if params["doc"]: + stream.write("_{}_\n".format(params["doc"])) + for param_name, info in params.items(): + if param_name == "section": + continue + if param_name == "doc": + continue + if param_name == "name": + continue + if param_name == "type": + continue + if info["suppressed"]: + continue + + param_type = demangle(info["type"]) + param_value = postprocess(info["value"], param_type) + stream.write("- {}\n".format(param_name)) + if info["doc"]: + stream.write(" - Description: {}\n".format(info["doc"])) + stream.write(" - Type: {}\n".format(param_type)) + if param_value: + stream.write(" - Default: {}\n".format(param_value)) stream.write("\n") stream.write("Details: [{}](@ref {})\n\n".format(input_type, type)) diff --git a/src/neml2/base/NEML2Object.cxx b/src/neml2/base/NEML2Object.cxx index e4a134c62a..b40fc95a78 100644 --- a/src/neml2/base/NEML2Object.cxx +++ b/src/neml2/base/NEML2Object.cxx @@ -30,6 +30,7 @@ NEML2Object::expected_options() { auto options = OptionSet(); options.set("_host") = nullptr; + options.set("_host").suppressed() = true; return options; } diff --git a/src/neml2/base/OptionSet.cxx b/src/neml2/base/OptionSet.cxx index f54e5213d9..499f9993a7 100644 --- a/src/neml2/base/OptionSet.cxx +++ b/src/neml2/base/OptionSet.cxx @@ -89,11 +89,16 @@ OptionSet::print(std::ostream & os) const { OptionSet::const_iterator it = _values.begin(); + os << " section: " << section() << '\n'; + os << " doc: " << doc() << '\n'; + while (it != _values.end()) { - os << " - " << it->first << ":\n"; - os << " type: " << it->second->type() << '\n'; - os << " value: "; + os << " " << it->first << ":\n"; + os << " type: " << it->second->type() << '\n'; + os << " doc: " << it->second->doc() << '\n'; + os << " suppressed: " << it->second->suppressed() << '\n'; + os << " value: "; it->second->print(os); if (++it != _values.end()) os << '\n'; diff --git a/src/neml2/base/Registry.cxx b/src/neml2/base/Registry.cxx index 46d7d5f0dd..fcd3dbc609 100644 --- a/src/neml2/base/Registry.cxx +++ b/src/neml2/base/Registry.cxx @@ -34,6 +34,13 @@ Registry::get() return registry_singleton; } +std::map +Registry::expected_options() +{ + auto & reg = get(); + return reg._expected_options; +} + OptionSet Registry::expected_options(const std::string & name) { @@ -45,6 +52,13 @@ Registry::expected_options(const std::string & name) return reg._expected_options.at(name); } +std::string +Registry::syntax_type(const std::string & type) +{ + auto & reg = get(); + return reg._syntax_type[type]; +} + BuildPtr Registry::builder(const std::string & name) { @@ -56,20 +70,6 @@ Registry::builder(const std::string & name) return reg._objects.at(name); } -// LCOV_EXCL_START -void -Registry::print(std::ostream & os) -{ - auto & reg = get(); - for (auto [type, options] : reg._expected_options) - { - options.set("type") = type; - os << "- " << reg._syntax_type[type] << ":\n"; - os << options << "\n"; - } -} -// LCOV_EXCL_STOP - void Registry::add_inner(const std::string & name, const std::string & type, diff --git a/src/neml2/drivers/Driver.cxx b/src/neml2/drivers/Driver.cxx index 861e00cf0f..36bed0ecca 100644 --- a/src/neml2/drivers/Driver.cxx +++ b/src/neml2/drivers/Driver.cxx @@ -30,6 +30,7 @@ OptionSet Driver::expected_options() { OptionSet options = NEML2Object::expected_options(); + options.section() = "Drivers"; options.set("verbose") = false; return options; } diff --git a/src/neml2/models/ArrheniusParameter.cxx b/src/neml2/models/ArrheniusParameter.cxx index 40075828ef..0dc59a3235 100644 --- a/src/neml2/models/ArrheniusParameter.cxx +++ b/src/neml2/models/ArrheniusParameter.cxx @@ -32,10 +32,23 @@ OptionSet ArrheniusParameter::expected_options() { OptionSet options = NonlinearParameter::expected_options(); + + options.doc() = "Defines the nonlinear parameter as a function of temperature according to the " + "Arrhenius law. The nonlinear parameter is therefore parametrized by the " + "reference value and the activation energy."; + options.set>("reference_value"); + options.set("reference_value").doc() = "reference value of the parameter"; + options.set>("activation_energy"); + options.set("activation_energy").doc() = "activation energy in the Arrhenius law"; + options.set("ideal_gas_constant"); + options.set("ideal_gas_constant").doc() = "the ideal gas constant"; + options.set("temperature") = VariableName("forces", "T"); + options.set("temperature").doc() = "variable name for the temperature"; + return options; } diff --git a/src/neml2/models/Data.cxx b/src/neml2/models/Data.cxx index b15d58c30c..7adafeba66 100644 --- a/src/neml2/models/Data.cxx +++ b/src/neml2/models/Data.cxx @@ -30,6 +30,7 @@ OptionSet Data::expected_options() { auto options = NEML2Object::expected_options(); + options.section() = "Data"; return options; } diff --git a/src/neml2/models/Model.cxx b/src/neml2/models/Model.cxx index 818277a5ea..8ca3f355c6 100644 --- a/src/neml2/models/Model.cxx +++ b/src/neml2/models/Model.cxx @@ -33,10 +33,17 @@ Model::expected_options() { OptionSet options = Data::expected_options(); options += NonlinearSystem::expected_options(); + + options.section() = "Models"; + options.set("use_AD_first_derivative") = false; options.set("use_AD_second_derivative") = false; options.set("_extra_derivative_order") = 0; options.set("_nonlinear_system") = false; + + options.set("_extra_derivative_order").suppressed() = true; + options.set("_nonlinear_system").suppressed() = true; + return options; } diff --git a/src/neml2/models/crystallography/user_tensors/FillMillerIndex.cxx b/src/neml2/models/crystallography/user_tensors/FillMillerIndex.cxx index eb020148e9..d6d9e78857 100644 --- a/src/neml2/models/crystallography/user_tensors/FillMillerIndex.cxx +++ b/src/neml2/models/crystallography/user_tensors/FillMillerIndex.cxx @@ -34,14 +34,14 @@ register_NEML2_object(FillMillerIndex); OptionSet FillMillerIndex::expected_options() { - OptionSet options = NEML2Object::expected_options(); + OptionSet options = UserTensor::expected_options(); options.set>("values"); return options; } FillMillerIndex::FillMillerIndex(const OptionSet & options) : MillerIndex(fill(options.get>("values"))), - NEML2Object(options) + UserTensor(options) { } diff --git a/src/neml2/models/crystallography/user_tensors/SymmetryFromOrbifold.cxx b/src/neml2/models/crystallography/user_tensors/SymmetryFromOrbifold.cxx index d94316bc60..8892001767 100644 --- a/src/neml2/models/crystallography/user_tensors/SymmetryFromOrbifold.cxx +++ b/src/neml2/models/crystallography/user_tensors/SymmetryFromOrbifold.cxx @@ -35,15 +35,15 @@ register_NEML2_object(SymmetryFromOrbifold); OptionSet SymmetryFromOrbifold::expected_options() { - OptionSet options = NEML2Object::expected_options(); + OptionSet options = UserTensor::expected_options(); options.set("orbifold"); return options; } SymmetryFromOrbifold::SymmetryFromOrbifold(const OptionSet & options) : R2(symmetry_operations_from_orbifold(options.get("orbifold"))), - NEML2Object(options) + UserTensor(options) { } } -} // namespace neml2 \ No newline at end of file +} // namespace neml2 diff --git a/src/neml2/solvers/Solver.cxx b/src/neml2/solvers/Solver.cxx index bbdeb81832..835c05a302 100644 --- a/src/neml2/solvers/Solver.cxx +++ b/src/neml2/solvers/Solver.cxx @@ -30,6 +30,7 @@ OptionSet Solver::expected_options() { OptionSet options = NEML2Object::expected_options(); + options.section() = "Solvers"; options.set("verbose"); return options; } diff --git a/src/neml2/tensors/user_tensors/EmptyBatchTensor.cxx b/src/neml2/tensors/user_tensors/EmptyBatchTensor.cxx index 3051df9930..5328897436 100644 --- a/src/neml2/tensors/user_tensors/EmptyBatchTensor.cxx +++ b/src/neml2/tensors/user_tensors/EmptyBatchTensor.cxx @@ -31,7 +31,7 @@ register_NEML2_object(EmptyBatchTensor); OptionSet EmptyBatchTensor::expected_options() { - OptionSet options = NEML2Object::expected_options(); + OptionSet options = UserTensor::expected_options(); options.set("batch_shape") = {}; options.set("base_shape") = {}; return options; @@ -41,7 +41,7 @@ EmptyBatchTensor::EmptyBatchTensor(const OptionSet & options) : BatchTensor(BatchTensor::empty(options.get("batch_shape"), options.get("base_shape"), default_tensor_options())), - NEML2Object(options) + UserTensor(options) { } } // namespace neml2 diff --git a/src/neml2/tensors/user_tensors/EmptyFixedDimTensor.cxx b/src/neml2/tensors/user_tensors/EmptyFixedDimTensor.cxx index 4b6704c80f..7814329a98 100644 --- a/src/neml2/tensors/user_tensors/EmptyFixedDimTensor.cxx +++ b/src/neml2/tensors/user_tensors/EmptyFixedDimTensor.cxx @@ -33,7 +33,7 @@ template OptionSet EmptyFixedDimTensor::expected_options() { - OptionSet options = NEML2Object::expected_options(); + OptionSet options = UserTensor::expected_options(); options.set("batch_shape") = {}; return options; } @@ -41,7 +41,7 @@ EmptyFixedDimTensor::expected_options() template EmptyFixedDimTensor::EmptyFixedDimTensor(const OptionSet & options) : T(T::empty(options.get("batch_shape"), default_tensor_options())), - NEML2Object(options) + UserTensor(options) { } diff --git a/src/neml2/tensors/user_tensors/Fill3DVec.cxx b/src/neml2/tensors/user_tensors/Fill3DVec.cxx index 8022a98e82..19da76f96a 100644 --- a/src/neml2/tensors/user_tensors/Fill3DVec.cxx +++ b/src/neml2/tensors/user_tensors/Fill3DVec.cxx @@ -31,14 +31,14 @@ register_NEML2_object(Fill3DVec); OptionSet Fill3DVec::expected_options() { - OptionSet options = NEML2Object::expected_options(); + OptionSet options = UserTensor::expected_options(); options.set>("values"); return options; } Fill3DVec::Fill3DVec(const OptionSet & options) : Vec(fill(options.get>("values"))), - NEML2Object(options) + UserTensor(options) { } diff --git a/src/neml2/tensors/user_tensors/FillR2.cxx b/src/neml2/tensors/user_tensors/FillR2.cxx index 385a5aee2c..759b630112 100644 --- a/src/neml2/tensors/user_tensors/FillR2.cxx +++ b/src/neml2/tensors/user_tensors/FillR2.cxx @@ -31,14 +31,14 @@ register_NEML2_object(FillR2); OptionSet FillR2::expected_options() { - OptionSet options = NEML2Object::expected_options(); + OptionSet options = UserTensor::expected_options(); options.set>>("values"); return options; } FillR2::FillR2(const OptionSet & options) : R2(fill(options.get>>("values"))), - NEML2Object(options) + UserTensor(options) { } diff --git a/src/neml2/tensors/user_tensors/FillRot.cxx b/src/neml2/tensors/user_tensors/FillRot.cxx index 755d470780..e7fa49e788 100644 --- a/src/neml2/tensors/user_tensors/FillRot.cxx +++ b/src/neml2/tensors/user_tensors/FillRot.cxx @@ -31,7 +31,7 @@ register_NEML2_object(FillRot); OptionSet FillRot::expected_options() { - OptionSet options = NEML2Object::expected_options(); + OptionSet options = UserTensor::expected_options(); options.set>>("values"); options.set("method") = "modified"; return options; @@ -40,7 +40,7 @@ FillRot::expected_options() FillRot::FillRot(const OptionSet & options) : Rot(fill(options.get>>("values"), options.get("method"))), - NEML2Object(options) + UserTensor(options) { } diff --git a/src/neml2/tensors/user_tensors/FillSR2.cxx b/src/neml2/tensors/user_tensors/FillSR2.cxx index f8296e828a..7ac486c8aa 100644 --- a/src/neml2/tensors/user_tensors/FillSR2.cxx +++ b/src/neml2/tensors/user_tensors/FillSR2.cxx @@ -31,14 +31,14 @@ register_NEML2_object(FillSR2); OptionSet FillSR2::expected_options() { - OptionSet options = NEML2Object::expected_options(); + OptionSet options = UserTensor::expected_options(); options.set>>("values"); return options; } FillSR2::FillSR2(const OptionSet & options) : SR2(fill(options.get>>("values"))), - NEML2Object(options) + UserTensor(options) { } diff --git a/src/neml2/tensors/user_tensors/FillWR2.cxx b/src/neml2/tensors/user_tensors/FillWR2.cxx index 54c2f8fd32..83676b5b64 100644 --- a/src/neml2/tensors/user_tensors/FillWR2.cxx +++ b/src/neml2/tensors/user_tensors/FillWR2.cxx @@ -31,14 +31,14 @@ register_NEML2_object(FillWR2); OptionSet FillWR2::expected_options() { - OptionSet options = NEML2Object::expected_options(); + OptionSet options = UserTensor::expected_options(); options.set>>("values"); return options; } FillWR2::FillWR2(const OptionSet & options) : WR2(fill(options.get>>("values"))), - NEML2Object(options) + UserTensor(options) { } diff --git a/src/neml2/tensors/user_tensors/FullBatchTensor.cxx b/src/neml2/tensors/user_tensors/FullBatchTensor.cxx index de4cdcfe83..158319b0ff 100644 --- a/src/neml2/tensors/user_tensors/FullBatchTensor.cxx +++ b/src/neml2/tensors/user_tensors/FullBatchTensor.cxx @@ -31,7 +31,7 @@ register_NEML2_object(FullBatchTensor); OptionSet FullBatchTensor::expected_options() { - OptionSet options = NEML2Object::expected_options(); + OptionSet options = UserTensor::expected_options(); options.set("batch_shape") = {}; options.set("base_shape") = {}; options.set("value"); @@ -43,7 +43,7 @@ FullBatchTensor::FullBatchTensor(const OptionSet & options) options.get("base_shape"), options.get("value"), default_tensor_options())), - NEML2Object(options) + UserTensor(options) { } } // namespace neml2 diff --git a/src/neml2/tensors/user_tensors/FullFixedDimTensor.cxx b/src/neml2/tensors/user_tensors/FullFixedDimTensor.cxx index 3e1676fef4..6beddea823 100644 --- a/src/neml2/tensors/user_tensors/FullFixedDimTensor.cxx +++ b/src/neml2/tensors/user_tensors/FullFixedDimTensor.cxx @@ -33,7 +33,7 @@ template OptionSet FullFixedDimTensor::expected_options() { - OptionSet options = NEML2Object::expected_options(); + OptionSet options = UserTensor::expected_options(); options.set("batch_shape") = {}; options.set("value"); return options; @@ -44,7 +44,7 @@ FullFixedDimTensor::FullFixedDimTensor(const OptionSet & options) : T(T::full(options.get("batch_shape"), options.get("value"), default_tensor_options())), - NEML2Object(options) + UserTensor(options) { } diff --git a/src/neml2/tensors/user_tensors/IdentityBatchTensor.cxx b/src/neml2/tensors/user_tensors/IdentityBatchTensor.cxx index 33413ff038..0834ed03b9 100644 --- a/src/neml2/tensors/user_tensors/IdentityBatchTensor.cxx +++ b/src/neml2/tensors/user_tensors/IdentityBatchTensor.cxx @@ -31,7 +31,7 @@ register_NEML2_object(IdentityBatchTensor); OptionSet IdentityBatchTensor::expected_options() { - OptionSet options = NEML2Object::expected_options(); + OptionSet options = UserTensor::expected_options(); options.set("batch_shape") = {}; options.set("n"); return options; @@ -41,7 +41,7 @@ IdentityBatchTensor::IdentityBatchTensor(const OptionSet & options) : BatchTensor(BatchTensor::identity(options.get("batch_shape"), options.get("n"), default_tensor_options())), - NEML2Object(options) + UserTensor(options) { } } // namespace neml2 diff --git a/src/neml2/tensors/user_tensors/LinspaceBatchTensor.cxx b/src/neml2/tensors/user_tensors/LinspaceBatchTensor.cxx index 8241726a89..88380ae8b6 100644 --- a/src/neml2/tensors/user_tensors/LinspaceBatchTensor.cxx +++ b/src/neml2/tensors/user_tensors/LinspaceBatchTensor.cxx @@ -32,7 +32,7 @@ register_NEML2_object(LinspaceBatchTensor); OptionSet LinspaceBatchTensor::expected_options() { - OptionSet options = NEML2Object::expected_options(); + OptionSet options = UserTensor::expected_options(); options.set>("start"); options.set>("end"); options.set("nstep"); @@ -48,7 +48,7 @@ LinspaceBatchTensor::LinspaceBatchTensor(const OptionSet & options) options.get("nstep"), options.get("dim"), options.get("batch_dim"))), - NEML2Object(options) + UserTensor(options) { auto bs = options.get("batch_expand"); if (bs.size() > 0) diff --git a/src/neml2/tensors/user_tensors/LinspaceFixedDimTensor.cxx b/src/neml2/tensors/user_tensors/LinspaceFixedDimTensor.cxx index e9c8fe63c9..705e742b27 100644 --- a/src/neml2/tensors/user_tensors/LinspaceFixedDimTensor.cxx +++ b/src/neml2/tensors/user_tensors/LinspaceFixedDimTensor.cxx @@ -33,7 +33,7 @@ template OptionSet LinspaceFixedDimTensor::expected_options() { - OptionSet options = NEML2Object::expected_options(); + OptionSet options = UserTensor::expected_options(); options.set>("start"); options.set>("end"); options.set("nstep"); @@ -49,7 +49,7 @@ LinspaceFixedDimTensor::LinspaceFixedDimTensor(const OptionSet & options) options.get("nstep"), options.get("dim"), options.get("batch_dim"))), - NEML2Object(options) + UserTensor(options) { } diff --git a/src/neml2/tensors/user_tensors/LogspaceBatchTensor.cxx b/src/neml2/tensors/user_tensors/LogspaceBatchTensor.cxx index 65d89c29fb..aac596842e 100644 --- a/src/neml2/tensors/user_tensors/LogspaceBatchTensor.cxx +++ b/src/neml2/tensors/user_tensors/LogspaceBatchTensor.cxx @@ -32,7 +32,7 @@ register_NEML2_object(LogspaceBatchTensor); OptionSet LogspaceBatchTensor::expected_options() { - OptionSet options = NEML2Object::expected_options(); + OptionSet options = UserTensor::expected_options(); options.set>("start"); options.set>("end"); options.set("nstep"); @@ -49,7 +49,7 @@ LogspaceBatchTensor::LogspaceBatchTensor(const OptionSet & options) options.get("dim"), options.get("batch_dim"), options.get("base"))), - NEML2Object(options) + UserTensor(options) { } } // namespace neml2 diff --git a/src/neml2/tensors/user_tensors/LogspaceFixedDimTensor.cxx b/src/neml2/tensors/user_tensors/LogspaceFixedDimTensor.cxx index 54fbfb4ccd..0a6c939606 100644 --- a/src/neml2/tensors/user_tensors/LogspaceFixedDimTensor.cxx +++ b/src/neml2/tensors/user_tensors/LogspaceFixedDimTensor.cxx @@ -33,7 +33,7 @@ template OptionSet LogspaceFixedDimTensor::expected_options() { - OptionSet options = NEML2Object::expected_options(); + OptionSet options = UserTensor::expected_options(); options.set>("start"); options.set>("end"); options.set("nstep"); @@ -51,7 +51,7 @@ LogspaceFixedDimTensor::LogspaceFixedDimTensor(const OptionSet & options) options.get("dim"), options.get("batch_dim"), options.get("base"))), - NEML2Object(options) + UserTensor(options) { } diff --git a/src/neml2/tensors/user_tensors/OnesBatchTensor.cxx b/src/neml2/tensors/user_tensors/OnesBatchTensor.cxx index c21c941e1e..465dc16ad4 100644 --- a/src/neml2/tensors/user_tensors/OnesBatchTensor.cxx +++ b/src/neml2/tensors/user_tensors/OnesBatchTensor.cxx @@ -31,7 +31,7 @@ register_NEML2_object(OnesBatchTensor); OptionSet OnesBatchTensor::expected_options() { - OptionSet options = NEML2Object::expected_options(); + OptionSet options = UserTensor::expected_options(); options.set("batch_shape") = {}; options.set("base_shape") = {}; return options; @@ -41,7 +41,7 @@ OnesBatchTensor::OnesBatchTensor(const OptionSet & options) : BatchTensor(BatchTensor::ones(options.get("batch_shape"), options.get("base_shape"), default_tensor_options())), - NEML2Object(options) + UserTensor(options) { } } // namespace neml2 diff --git a/src/neml2/tensors/user_tensors/OnesFixedDimTensor.cxx b/src/neml2/tensors/user_tensors/OnesFixedDimTensor.cxx index af394198d6..7803256300 100644 --- a/src/neml2/tensors/user_tensors/OnesFixedDimTensor.cxx +++ b/src/neml2/tensors/user_tensors/OnesFixedDimTensor.cxx @@ -33,7 +33,7 @@ template OptionSet OnesFixedDimTensor::expected_options() { - OptionSet options = NEML2Object::expected_options(); + OptionSet options = UserTensor::expected_options(); options.set("batch_shape") = {}; return options; } @@ -41,7 +41,7 @@ OnesFixedDimTensor::expected_options() template OnesFixedDimTensor::OnesFixedDimTensor(const OptionSet & options) : T(T::ones(options.get("batch_shape"), default_tensor_options())), - NEML2Object(options) + UserTensor(options) { } diff --git a/src/neml2/tensors/user_tensors/Orientation.cxx b/src/neml2/tensors/user_tensors/Orientation.cxx index a33e8a3b3e..24d14cef18 100644 --- a/src/neml2/tensors/user_tensors/Orientation.cxx +++ b/src/neml2/tensors/user_tensors/Orientation.cxx @@ -35,7 +35,7 @@ register_NEML2_object(Orientation); OptionSet Orientation::expected_options() { - OptionSet options = NEML2Object::expected_options(); + OptionSet options = UserTensor::expected_options(); options.set("input_type") = "euler_angles"; options.set("angle_convention") = "kocks"; options.set("angle_type") = "degrees"; @@ -50,7 +50,7 @@ Orientation::expected_options() Orientation::Orientation(const OptionSet & options) : Rot(fill(options)), - NEML2Object(options) + UserTensor(options) { } diff --git a/src/neml2/tensors/user_tensors/UserBatchTensor.cxx b/src/neml2/tensors/user_tensors/UserBatchTensor.cxx index bd9210a3b2..d91cfa961f 100644 --- a/src/neml2/tensors/user_tensors/UserBatchTensor.cxx +++ b/src/neml2/tensors/user_tensors/UserBatchTensor.cxx @@ -31,7 +31,7 @@ register_NEML2_object_alias(UserBatchTensor, "BatchTensor"); OptionSet UserBatchTensor::expected_options() { - OptionSet options = NEML2Object::expected_options(); + OptionSet options = UserTensor::expected_options(); options.set>("values"); options.set("batch_shape") = {}; options.set("base_shape") = {}; @@ -42,7 +42,7 @@ UserBatchTensor::UserBatchTensor(const OptionSet & options) : BatchTensor(BatchTensor::empty(options.get("batch_shape"), options.get("base_shape"), default_tensor_options())), - NEML2Object(options) + UserTensor(options) { auto vals = options.get>("values"); auto flat = torch::tensor(vals, default_tensor_options()); diff --git a/src/neml2/tensors/user_tensors/UserFixedDimTensor.cxx b/src/neml2/tensors/user_tensors/UserFixedDimTensor.cxx index ff4a1dbcf1..2fe3374e17 100644 --- a/src/neml2/tensors/user_tensors/UserFixedDimTensor.cxx +++ b/src/neml2/tensors/user_tensors/UserFixedDimTensor.cxx @@ -33,7 +33,7 @@ template OptionSet UserFixedDimTensor::expected_options() { - OptionSet options = NEML2Object::expected_options(); + OptionSet options = UserTensor::expected_options(); options.set>("values"); options.set("batch_shape") = {}; return options; @@ -42,7 +42,7 @@ UserFixedDimTensor::expected_options() template UserFixedDimTensor::UserFixedDimTensor(const OptionSet & options) : T(T::empty(options.get("batch_shape"), default_tensor_options())), - NEML2Object(options) + UserTensor(options) { auto vals = options.get>("values"); auto flat = torch::tensor(vals, default_tensor_options()); diff --git a/src/neml2/tensors/user_tensors/UserTensor.cxx b/src/neml2/tensors/user_tensors/UserTensor.cxx new file mode 100644 index 0000000000..192fa45bcd --- /dev/null +++ b/src/neml2/tensors/user_tensors/UserTensor.cxx @@ -0,0 +1,41 @@ +// Copyright 2023, UChicago Argonne, LLC +// All Rights Reserved +// Software Name: NEML2 -- the New Engineering material Model Library, version 2 +// By: Argonne National Laboratory +// OPEN SOURCE LICENSE (MIT) +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. + +#include "neml2/tensors/user_tensors/UserTensor.h" + +namespace neml2 +{ +OptionSet +UserTensor::expected_options() +{ + OptionSet options = NEML2Object::expected_options(); + options.section() = "Tensors"; + return options; +} + +UserTensor::UserTensor(const OptionSet & options) + : NEML2Object(options) +{ +} +} // namespace neml2 diff --git a/src/neml2/tensors/user_tensors/ZerosBatchTensor.cxx b/src/neml2/tensors/user_tensors/ZerosBatchTensor.cxx index 764cfa126b..64acf93ea6 100644 --- a/src/neml2/tensors/user_tensors/ZerosBatchTensor.cxx +++ b/src/neml2/tensors/user_tensors/ZerosBatchTensor.cxx @@ -31,7 +31,7 @@ register_NEML2_object(ZerosBatchTensor); OptionSet ZerosBatchTensor::expected_options() { - OptionSet options = NEML2Object::expected_options(); + OptionSet options = UserTensor::expected_options(); options.set("batch_shape") = {}; options.set("base_shape") = {}; return options; @@ -41,7 +41,7 @@ ZerosBatchTensor::ZerosBatchTensor(const OptionSet & options) : BatchTensor(BatchTensor::zeros(options.get("batch_shape"), options.get("base_shape"), default_tensor_options())), - NEML2Object(options) + UserTensor(options) { } } // namespace neml2 diff --git a/src/neml2/tensors/user_tensors/ZerosFixedDimTensor.cxx b/src/neml2/tensors/user_tensors/ZerosFixedDimTensor.cxx index e9c07c786c..f24e533180 100644 --- a/src/neml2/tensors/user_tensors/ZerosFixedDimTensor.cxx +++ b/src/neml2/tensors/user_tensors/ZerosFixedDimTensor.cxx @@ -33,7 +33,7 @@ template OptionSet ZerosFixedDimTensor::expected_options() { - OptionSet options = NEML2Object::expected_options(); + OptionSet options = UserTensor::expected_options(); options.set("batch_shape") = {}; return options; } @@ -41,7 +41,7 @@ ZerosFixedDimTensor::expected_options() template ZerosFixedDimTensor::ZerosFixedDimTensor(const OptionSet & options) : T(T::zeros(options.get("batch_shape"), default_tensor_options())), - NEML2Object(options) + UserTensor(options) { } From 8e976868de6a5d758a150539c71ab4a5a8b7edfe Mon Sep 17 00:00:00 2001 From: Gary Hu Date: Thu, 2 May 2024 22:36:16 -0500 Subject: [PATCH 05/55] Change Catch2 from git submodule to CMake FetchContent; Upgrade from Catch2 v2 to v3 --- .github/workflows/tests.yml | 5 +- .gitmodules | 4 - doc/content/_01_install.md | 2 +- extern/Catch2 | 1 - tests/CMakeLists.txt | 81 ++++++++----------- tests/benchmark/chaboche/chaboche.cxx | 4 +- tests/benchmark/main.cxx | 28 ------- .../taylor_rolling_fcc/taylor_rolling_fcc.cxx | 4 +- .../taylor_single_orientation.cxx | 4 +- tests/regression/main.cxx | 27 ------- .../regression_solid_mechanics.cxx | 2 +- tests/unit/base/test_CrossRef.cxx | 7 +- tests/unit/base/test_Factory.cxx | 2 +- tests/unit/base/test_HITParser.cxx | 14 ++-- tests/unit/base/test_OptionSet.cxx | 8 +- .../crystallography/test_CrystalGeometry.cxx | 2 +- .../crystallography/test_CubicCrystal.cxx | 2 +- .../unit/crystallography/test_MillerIndex.cxx | 2 +- .../user_tensors/test_FillMillerIndex.cxx | 2 +- .../test_SymmetryFromOrbifold.cxx | 3 +- .../test_SolidMechanicsDriver.cxx | 2 +- tests/unit/main.cxx | 27 ------- tests/unit/misc/test_parser_utils.cxx | 10 ++- tests/unit/misc/test_utils.cxx | 2 +- tests/unit/models/test_ParameterStore.cxx | 10 ++- tests/unit/models/test_models.cxx | 2 +- tests/unit/solvers/test_NonlinearSolvers.cxx | 3 +- tests/unit/solvers/test_NonlinearSystem.cxx | 5 +- tests/unit/tensors/test_BatchTensor.cxx | 2 +- tests/unit/tensors/test_BatchTensorBase.cxx | 11 +-- tests/unit/tensors/test_FixedDimTensor.cxx | 9 ++- tests/unit/tensors/test_LabeledAxis.cxx | 2 +- .../unit/tensors/test_LabeledAxisAccesor.cxx | 5 +- tests/unit/tensors/test_LabeledTensor.cxx | 39 ++++----- tests/unit/tensors/test_Quaternion.cxx | 2 +- tests/unit/tensors/test_R2.cxx | 2 +- tests/unit/tensors/test_R3.cxx | 2 +- tests/unit/tensors/test_R4.cxx | 2 +- tests/unit/tensors/test_Rot.cxx | 2 +- tests/unit/tensors/test_SR2.cxx | 2 +- tests/unit/tensors/test_SSR4.cxx | 2 +- tests/unit/tensors/test_Scalar.cxx | 2 +- tests/unit/tensors/test_Vec.cxx | 2 +- tests/unit/tensors/test_VecBase.cxx | 2 +- tests/unit/tensors/test_WR2.cxx | 2 +- tests/unit/tensors/test_WWR4.cxx | 2 +- tests/unit/tensors/test_list_tensors.cxx | 2 +- .../test_symmetry_operator_definitions.cxx | 2 +- tests/unit/tensors/test_vector_transform.cxx | 2 +- .../user_tensors/test_EmptyBatchTensor.cxx | 2 +- .../user_tensors/test_EmptyFixedDimTensor.cxx | 2 +- .../tensors/user_tensors/test_Fill3DVec.cxx | 2 +- .../unit/tensors/user_tensors/test_FillR2.cxx | 2 +- .../tensors/user_tensors/test_FillSR2.cxx | 2 +- .../user_tensors/test_FullBatchTensor.cxx | 2 +- .../user_tensors/test_FullFixedDimTensor.cxx | 2 +- .../user_tensors/test_IdentityBatchTensor.cxx | 2 +- .../user_tensors/test_LinspaceBatchTensor.cxx | 2 +- .../test_LinspaceFixedDimTensor.cxx | 2 +- .../user_tensors/test_LogspaceBatchTensor.cxx | 2 +- .../test_LogspaceFixedDimTensor.cxx | 2 +- .../user_tensors/test_OnesBatchTensor.cxx | 2 +- .../user_tensors/test_OnesFixedDimTensor.cxx | 2 +- .../tensors/user_tensors/test_Orientation.cxx | 2 +- .../user_tensors/test_UserBatchTensor.cxx | 7 +- .../user_tensors/test_UserFixedDimTensor.cxx | 2 +- .../user_tensors/test_ZerosBatchTensor.cxx | 2 +- .../user_tensors/test_ZerosFixedDimTensor.cxx | 2 +- tests/verification/main.cxx | 27 ------- .../verification_solid_mechanics.cxx | 2 +- 70 files changed, 160 insertions(+), 275 deletions(-) delete mode 160000 extern/Catch2 delete mode 100644 tests/benchmark/main.cxx delete mode 100644 tests/regression/main.cxx delete mode 100644 tests/unit/main.cxx delete mode 100644 tests/verification/main.cxx diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml index 0a40130567..2069045a6b 100644 --- a/.github/workflows/tests.yml +++ b/.github/workflows/tests.yml @@ -182,7 +182,6 @@ jobs: echo -e "\ #include \"neml2/base/Registry.h\"\n\ int main() {\n\ - neml2::Registry::print(std::cout);\n\ return 0;\n\ }\ " > main.cxx @@ -190,7 +189,7 @@ jobs: - name: Create a CMakeLists.txt file for testing purposes run: | echo -e "\ - cmake_minimum_required(VERSION 3.5) + cmake_minimum_required(VERSION 3.23) project(FOO)\n\ add_subdirectory(neml2)\n\ add_executable(foo main.cxx)\n\ @@ -198,7 +197,7 @@ jobs: " > CMakeLists.txt - run: cat CMakeLists.txt - name: Configure with CMake - run: cmake -DCMAKE_BUILD_TYPE=Release -DBUILD_TESTING=OFF -DNEML2_DOC=OFF -B build . + run: cmake -DNEML2_TESTS=OFF -B build . - name: Compile run: cd build && make -j 2 - run: cd build && ./foo diff --git a/.gitmodules b/.gitmodules index 9487632756..4ce79193f2 100644 --- a/.gitmodules +++ b/.gitmodules @@ -1,7 +1,3 @@ -[submodule "extern/Catch2"] - path = extern/Catch2 - url = https://github.com/catchorg/Catch2.git - ignore = dirty [submodule "extern/hit/hit"] path = extern/hit/hit url = https://github.com/idaholab/hit.git diff --git a/doc/content/_01_install.md b/doc/content/_01_install.md index dadb398383..57acce6db5 100644 --- a/doc/content/_01_install.md +++ b/doc/content/_01_install.md @@ -91,7 +91,7 @@ Commonly used configuration options are summarized below. Default options are un | CMAKE_UNITY_BUILD | | CMake [Reference](https://cmake.org/cmake/help/latest/variable/CMAKE_UNITY_BUILD.html) | | NEML2_DTYPE | Float16, Float32, Float64 | Default floating point integral type used in the material models | | NEML2_INT_DTYPE | Int8, Int16, Int32, Int64 | Default fixed point integral type used in the material models | -| BUILD_TESTING | ON, OFF | Master knob for including/excluding all tests | +| NEML2_TESTS | ON, OFF | Master knob for including/excluding all tests | | NEML2_UNIT | ON, OFF | Create the unit testing target | | NEML2_REGRESSION | ON, OFF | Create the regression testing target | | NEML2_VERIFICATION | ON, OFF | Create the verification testing target | diff --git a/extern/Catch2 b/extern/Catch2 deleted file mode 160000 index 20ace55034..0000000000 --- a/extern/Catch2 +++ /dev/null @@ -1 +0,0 @@ -Subproject commit 20ace5503422a8511036aa9d486435041127e0cf diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt index 59292a6077..24355b1f09 100644 --- a/tests/CMakeLists.txt +++ b/tests/CMakeLists.txt @@ -1,11 +1,20 @@ include(NEML2UnityGroup) # Catch2, for testing -add_subdirectory(${NEML2_SOURCE_DIR}/extern/Catch2 ${NEML2_BINARY_DIR}/extern/Catch2) -list(APPEND CMAKE_MODULE_PATH ${NEML2_SOURCE_DIR}/extern/Catch2/contrib) - -# include(CTest) -include(Catch) +FetchContent_Declare( + Catch2 + GIT_REPOSITORY https://github.com/catchorg/Catch2.git + GIT_TAG b5373dadca40b7edc8570cf9470b9b1cb1934d40 # version 3.5.4 +) +message(STATUS "Downloading Catch2, this may take a few minutes.") +FetchContent_MakeAvailable(Catch2) + +# Catch2 v3 is built as a static library, so we MUST make sure the compile definitions are compatible with ours +if(Torch_CXX11_ABI) + target_compile_definitions(Catch2 PUBLIC _GLIBCXX_USE_CXX11_ABI=1) +else() + target_compile_definitions(Catch2 PUBLIC _GLIBCXX_USE_CXX11_ABI=0) +endif() # Test utilities add_subdirectory(src) @@ -17,17 +26,12 @@ option(NEML2_UNIT "Build NEML2 unit tests" ON) if(NEML2_UNIT) file(GLOB_RECURSE UNIT_TESTS unit/*.cxx) - add_executable(unit_tests - ${TEST_UTILS} - ${UNIT_TESTS} - ) + add_executable(unit_tests ${UNIT_TESTS}) - target_compile_options(unit_tests PUBLIC -Wall -Wextra -pedantic -Werror) + target_compile_options(unit_tests PRIVATE -Wall -Wextra -pedantic -Werror) register_unity_group(unit_tests "Unit test" unit) - target_link_libraries(unit_tests Catch2::Catch2) - target_link_libraries(unit_tests neml2 testutils) - target_include_directories(unit_tests PUBLIC ${NEML2_SOURCE_DIR}/tests/include) - catch_discover_tests(unit_tests) + target_link_libraries(unit_tests PRIVATE testutils) + target_link_libraries(unit_tests PRIVATE Catch2::Catch2WithMain) if(NOT ${NEML2_BINARY_DIR} STREQUAL ${NEML2_SOURCE_DIR}) add_custom_command(TARGET unit_tests @@ -58,17 +62,12 @@ option(NEML2_REGRESSION "Build NEML2 regression tests" ON) if(NEML2_REGRESSION) file(GLOB_RECURSE REGRESSION_TESTS regression/*.cxx) - add_executable(regression_tests - ${TEST_UTILS} - ${REGRESSION_TESTS} - ) + add_executable(regression_tests ${REGRESSION_TESTS}) - target_compile_options(regression_tests PUBLIC -Wall -Wextra -pedantic -Werror) + target_compile_options(regression_tests PRIVATE -Wall -Wextra -pedantic -Werror) register_unity_group(regression_tests "Regression test" regression) - target_link_libraries(regression_tests Catch2::Catch2) - target_link_libraries(regression_tests neml2 testutils) - target_include_directories(regression_tests PUBLIC "${NEML2_SOURCE_DIR}/tests/include") - catch_discover_tests(regression_tests) + target_link_libraries(regression_tests PRIVATE testutils) + target_link_libraries(regression_tests PRIVATE Catch2::Catch2WithMain) if(NOT ${NEML2_BINARY_DIR} STREQUAL ${NEML2_SOURCE_DIR}) add_custom_command(TARGET regression_tests @@ -99,17 +98,12 @@ option(NEML2_VERIFICATION "Build NEML2 verification tests" ON) if(NEML2_VERIFICATION) file(GLOB_RECURSE VERIFICATION_TESTS verification/*.cxx) - add_executable(verification_tests - ${TEST_UTILS} - ${VERIFICATION_TESTS} - ) + add_executable(verification_tests ${VERIFICATION_TESTS}) - target_compile_options(verification_tests PUBLIC -Wall -Wextra -pedantic -Werror) + target_compile_options(verification_tests PRIVATE -Wall -Wextra -pedantic -Werror) register_unity_group(verification_tests "Verification test" verification) - target_link_libraries(verification_tests Catch2::Catch2) - target_link_libraries(verification_tests neml2 testutils) - target_include_directories(verification_tests PUBLIC "${NEML2_SOURCE_DIR}/tests/include") - catch_discover_tests(verification_tests) + target_link_libraries(verification_tests PRIVATE testutils) + target_link_libraries(verification_tests PRIVATE Catch2::Catch2WithMain) if(NOT ${NEML2_BINARY_DIR} STREQUAL ${NEML2_SOURCE_DIR}) add_custom_command(TARGET verification_tests @@ -140,19 +134,14 @@ option(NEML2_BENCHMARK "Build NEML2 benchmark tests" OFF) if(NEML2_BENCHMARK) file(GLOB_RECURSE BENCHMARK_TESTS benchmark/*.cxx) - add_executable(benchmark_tests - ${TEST_UTILS} - ${BENCHMARK_TESTS} - ) + add_executable(benchmark_tests ${BENCHMARK_TESTS}) # compile options - target_compile_options(benchmark_tests PUBLIC -Wall -Wextra -pedantic -Werror) + target_compile_options(benchmark_tests PRIVATE -Wall -Wextra -pedantic -Werror) register_unity_group(benchmark_tests "Benchmark test" benchmark) - target_link_libraries(benchmark_tests Catch2::Catch2) - target_link_libraries(benchmark_tests neml2 testutils) - target_include_directories(benchmark_tests PUBLIC "${NEML2_SOURCE_DIR}/tests/include") - catch_discover_tests(benchmark_tests) + target_link_libraries(benchmark_tests PRIVATE testutils) + target_link_libraries(benchmark_tests PRIVATE Catch2::Catch2WithMain) if(NOT ${NEML2_BINARY_DIR} STREQUAL ${NEML2_SOURCE_DIR}) add_custom_command(TARGET benchmark_tests @@ -185,16 +174,12 @@ if(NEML2_PROFILING) # gperftools for profiling add_subdirectory(${NEML2_SOURCE_DIR}/extern/gperftools ${NEML2_BINARY_DIR}/extern/gperftools EXCLUDE_FROM_ALL) file(GLOB_RECURSE PROFILING_TESTS profiling/*.cxx) - add_executable(profiling_tests - ${TEST_UTILS} - ${PROFILING_TESTS} - ) + add_executable(profiling_tests ${PROFILING_TESTS}) register_unity_group(profiling_tests "Profiling test" profiling) - target_compile_options(profiling_tests PUBLIC -Wall -Wextra -pedantic -Werror) + target_compile_options(profiling_tests PRIVATE -Wall -Wextra -pedantic -Werror) target_link_options(profiling_tests PRIVATE "-Wl,-no-as-needed") - target_link_libraries(profiling_tests neml2 testutils profiler) - target_include_directories(profiling_tests PUBLIC "${NEML2_SOURCE_DIR}/tests/include") + target_link_libraries(profiling_tests PRIVATE testutils profiler) if(NOT ${NEML2_BINARY_DIR} STREQUAL ${NEML2_SOURCE_DIR}) add_custom_command(TARGET profiling_tests diff --git a/tests/benchmark/chaboche/chaboche.cxx b/tests/benchmark/chaboche/chaboche.cxx index a4d2ea8350..b7f26b9219 100644 --- a/tests/benchmark/chaboche/chaboche.cxx +++ b/tests/benchmark/chaboche/chaboche.cxx @@ -22,9 +22,7 @@ // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN // THE SOFTWARE. -#define CATCH_CONFIG_ENABLE_BENCHMARKING - -#include +#include #include "utils.h" #include "neml2/drivers/Driver.h" diff --git a/tests/benchmark/main.cxx b/tests/benchmark/main.cxx deleted file mode 100644 index 95ff4a12e5..0000000000 --- a/tests/benchmark/main.cxx +++ /dev/null @@ -1,28 +0,0 @@ -// Copyright 2023, UChicago Argonne, LLC -// All Rights Reserved -// Software Name: NEML2 -- the New Engineering material Model Library, version 2 -// By: Argonne National Laboratory -// OPEN SOURCE LICENSE (MIT) -// -// Permission is hereby granted, free of charge, to any person obtaining a copy -// of this software and associated documentation files (the "Software"), to deal -// in the Software without restriction, including without limitation the rights -// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -// copies of the Software, and to permit persons to whom the Software is -// furnished to do so, subject to the following conditions: -// -// The above copyright notice and this permission notice shall be included in -// all copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -// THE SOFTWARE. - -#define CATCH_CONFIG_MAIN -#define CATCH_CONFIG_ENABLE_BENCHMARKING - -#include diff --git a/tests/benchmark/taylor_rolling_fcc/taylor_rolling_fcc.cxx b/tests/benchmark/taylor_rolling_fcc/taylor_rolling_fcc.cxx index 3b43cfb720..edd824892e 100644 --- a/tests/benchmark/taylor_rolling_fcc/taylor_rolling_fcc.cxx +++ b/tests/benchmark/taylor_rolling_fcc/taylor_rolling_fcc.cxx @@ -22,9 +22,7 @@ // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN // THE SOFTWARE. -#define CATCH_CONFIG_ENABLE_BENCHMARKING - -#include +#include #include "utils.h" #include "neml2/drivers/Driver.h" diff --git a/tests/benchmark/taylor_single_orientation/taylor_single_orientation.cxx b/tests/benchmark/taylor_single_orientation/taylor_single_orientation.cxx index 08d36bd218..610f1df295 100644 --- a/tests/benchmark/taylor_single_orientation/taylor_single_orientation.cxx +++ b/tests/benchmark/taylor_single_orientation/taylor_single_orientation.cxx @@ -22,9 +22,7 @@ // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN // THE SOFTWARE. -#define CATCH_CONFIG_ENABLE_BENCHMARKING - -#include +#include #include "utils.h" #include "neml2/drivers/Driver.h" diff --git a/tests/regression/main.cxx b/tests/regression/main.cxx deleted file mode 100644 index 740a4fd06a..0000000000 --- a/tests/regression/main.cxx +++ /dev/null @@ -1,27 +0,0 @@ -// Copyright 2023, UChicago Argonne, LLC -// All Rights Reserved -// Software Name: NEML2 -- the New Engineering material Model Library, version 2 -// By: Argonne National Laboratory -// OPEN SOURCE LICENSE (MIT) -// -// Permission is hereby granted, free of charge, to any person obtaining a copy -// of this software and associated documentation files (the "Software"), to deal -// in the Software without restriction, including without limitation the rights -// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -// copies of the Software, and to permit persons to whom the Software is -// furnished to do so, subject to the following conditions: -// -// The above copyright notice and this permission notice shall be included in -// all copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -// THE SOFTWARE. - -#define CATCH_CONFIG_MAIN - -#include diff --git a/tests/regression/solid_mechanics/regression_solid_mechanics.cxx b/tests/regression/solid_mechanics/regression_solid_mechanics.cxx index 0800b052e8..5e578115a3 100644 --- a/tests/regression/solid_mechanics/regression_solid_mechanics.cxx +++ b/tests/regression/solid_mechanics/regression_solid_mechanics.cxx @@ -22,7 +22,7 @@ // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN // THE SOFTWARE. -#include +#include #include #include "utils.h" diff --git a/tests/unit/base/test_CrossRef.cxx b/tests/unit/base/test_CrossRef.cxx index b0942618dc..1d58b9c5c6 100644 --- a/tests/unit/base/test_CrossRef.cxx +++ b/tests/unit/base/test_CrossRef.cxx @@ -22,7 +22,8 @@ // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN // THE SOFTWARE. -#include +#include +#include #include "utils.h" #include "neml2/misc/math.h" @@ -55,7 +56,7 @@ TEST_CASE("CrossRef", "[base]") SECTION("empty scalar") { REQUIRE_THROWS_WITH(load_model("unit/base/test_CrossRef_empty_Scalar.i"), - Catch::Matchers::Contains("Failed to parse '' as a")); + Catch::Matchers::ContainsSubstring("Failed to parse '' as a")); } SECTION("SR2 operator=") @@ -75,6 +76,6 @@ TEST_CASE("CrossRef", "[base]") SECTION("empty tensor") { REQUIRE_THROWS_WITH(load_model("unit/base/test_CrossRef_empty_Tensor.i"), - Catch::Matchers::Contains("Failed to parse '' as a")); + Catch::Matchers::ContainsSubstring("Failed to parse '' as a")); } } diff --git a/tests/unit/base/test_Factory.cxx b/tests/unit/base/test_Factory.cxx index 5eaa061b0e..f58fbcd715 100644 --- a/tests/unit/base/test_Factory.cxx +++ b/tests/unit/base/test_Factory.cxx @@ -21,7 +21,7 @@ // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN // THE SOFTWARE. -#include +#include #include "neml2/models/SumModel.h" diff --git a/tests/unit/base/test_HITParser.cxx b/tests/unit/base/test_HITParser.cxx index 8e7edb7713..4d73b9459a 100644 --- a/tests/unit/base/test_HITParser.cxx +++ b/tests/unit/base/test_HITParser.cxx @@ -21,7 +21,9 @@ // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN // THE SOFTWARE. -#include + +#include +#include #include "neml2/base/HITParser.h" #include "SampleParserTestingModel.h" @@ -81,7 +83,7 @@ TEST_CASE("HITParser", "[base]") SECTION("Reals") { - REQUIRE(options.get("Real") == Approx(3.14159)); + REQUIRE(options.get("Real") == Catch::Approx(3.14159)); REQUIRE_THAT(options.get>("Real_vec"), Catch::Matchers::Approx(std::vector{-111, 12, 1.1})); REQUIRE_THAT(options.get>>("Real_vec_vec")[0], @@ -123,10 +125,10 @@ TEST_CASE("HITParser", "[base]") { SECTION("setting a suppressed option") { - REQUIRE_THROWS_WITH( - parser.parse("unit/base/test_HITParser2.i"), - Catch::Matchers::Contains("Option named 'suppressed_option' is suppressed, and its " - "value cannot be modified.")); + REQUIRE_THROWS_WITH(parser.parse("unit/base/test_HITParser2.i"), + Catch::Matchers::ContainsSubstring( + "Option named 'suppressed_option' is suppressed, and its " + "value cannot be modified.")); } } } diff --git a/tests/unit/base/test_OptionSet.cxx b/tests/unit/base/test_OptionSet.cxx index c48029b1cc..d5e0e845de 100644 --- a/tests/unit/base/test_OptionSet.cxx +++ b/tests/unit/base/test_OptionSet.cxx @@ -21,7 +21,9 @@ // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN // THE SOFTWARE. -#include + +#include +#include #include "neml2/base/OptionSet.h" @@ -42,7 +44,7 @@ TEST_CASE("OptionSet", "[base]") SECTION("get") { - REQUIRE(options.get("p1") == Approx(1.5)); + REQUIRE(options.get("p1") == Catch::Approx(1.5)); REQUIRE(options.get("p2") == "foo"); REQUIRE(options.get("p3") == 3); REQUIRE_THAT(options.get>("p4"), @@ -70,7 +72,7 @@ TEST_CASE("OptionSet", "[base]") SECTION("copy") { OptionSet options2(options); - REQUIRE(options.get("p1") == Approx(1.5)); + REQUIRE(options.get("p1") == Catch::Approx(1.5)); REQUIRE(options.get("p2") == "foo"); REQUIRE(options.get("p3") == 3); REQUIRE_THAT(options.get>("p4"), diff --git a/tests/unit/crystallography/test_CrystalGeometry.cxx b/tests/unit/crystallography/test_CrystalGeometry.cxx index db5d70d907..f7382b44de 100644 --- a/tests/unit/crystallography/test_CrystalGeometry.cxx +++ b/tests/unit/crystallography/test_CrystalGeometry.cxx @@ -22,7 +22,7 @@ // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN // THE SOFTWARE. -#include +#include #include diff --git a/tests/unit/crystallography/test_CubicCrystal.cxx b/tests/unit/crystallography/test_CubicCrystal.cxx index e844dfed64..73bf744589 100644 --- a/tests/unit/crystallography/test_CubicCrystal.cxx +++ b/tests/unit/crystallography/test_CubicCrystal.cxx @@ -22,7 +22,7 @@ // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN // THE SOFTWARE. -#include +#include #include diff --git a/tests/unit/crystallography/test_MillerIndex.cxx b/tests/unit/crystallography/test_MillerIndex.cxx index d7f324be6c..676e5ced97 100644 --- a/tests/unit/crystallography/test_MillerIndex.cxx +++ b/tests/unit/crystallography/test_MillerIndex.cxx @@ -22,7 +22,7 @@ // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN // THE SOFTWARE. -#include +#include #include "neml2/models/crystallography/MillerIndex.h" #include "neml2/tensors/tensors.h" diff --git a/tests/unit/crystallography/user_tensors/test_FillMillerIndex.cxx b/tests/unit/crystallography/user_tensors/test_FillMillerIndex.cxx index f937259128..cc6c697be0 100644 --- a/tests/unit/crystallography/user_tensors/test_FillMillerIndex.cxx +++ b/tests/unit/crystallography/user_tensors/test_FillMillerIndex.cxx @@ -22,7 +22,7 @@ // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN // THE SOFTWARE. -#include +#include #include "utils.h" #include "neml2/models/crystallography/user_tensors/FillMillerIndex.h" diff --git a/tests/unit/crystallography/user_tensors/test_SymmetryFromOrbifold.cxx b/tests/unit/crystallography/user_tensors/test_SymmetryFromOrbifold.cxx index 1d1d1c2cf7..370e9f8ce6 100644 --- a/tests/unit/crystallography/user_tensors/test_SymmetryFromOrbifold.cxx +++ b/tests/unit/crystallography/user_tensors/test_SymmetryFromOrbifold.cxx @@ -22,7 +22,8 @@ // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN // THE SOFTWARE. -#include +#include +#include #include diff --git a/tests/unit/drivers/solid_mechanics/test_SolidMechanicsDriver.cxx b/tests/unit/drivers/solid_mechanics/test_SolidMechanicsDriver.cxx index 3ad1ca8e89..6716c068ba 100644 --- a/tests/unit/drivers/solid_mechanics/test_SolidMechanicsDriver.cxx +++ b/tests/unit/drivers/solid_mechanics/test_SolidMechanicsDriver.cxx @@ -22,7 +22,7 @@ // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN // THE SOFTWARE. -#include +#include #include "utils.h" #include "neml2/drivers/solid_mechanics/SolidMechanicsDriver.h" diff --git a/tests/unit/main.cxx b/tests/unit/main.cxx deleted file mode 100644 index 740a4fd06a..0000000000 --- a/tests/unit/main.cxx +++ /dev/null @@ -1,27 +0,0 @@ -// Copyright 2023, UChicago Argonne, LLC -// All Rights Reserved -// Software Name: NEML2 -- the New Engineering material Model Library, version 2 -// By: Argonne National Laboratory -// OPEN SOURCE LICENSE (MIT) -// -// Permission is hereby granted, free of charge, to any person obtaining a copy -// of this software and associated documentation files (the "Software"), to deal -// in the Software without restriction, including without limitation the rights -// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -// copies of the Software, and to permit persons to whom the Software is -// furnished to do so, subject to the following conditions: -// -// The above copyright notice and this permission notice shall be included in -// all copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -// THE SOFTWARE. - -#define CATCH_CONFIG_MAIN - -#include diff --git a/tests/unit/misc/test_parser_utils.cxx b/tests/unit/misc/test_parser_utils.cxx index 2bc965f88d..04e6bc93dd 100644 --- a/tests/unit/misc/test_parser_utils.cxx +++ b/tests/unit/misc/test_parser_utils.cxx @@ -21,7 +21,9 @@ // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN // THE SOFTWARE. -#include + +#include +#include #include "neml2/misc/parser_utils.h" @@ -60,7 +62,7 @@ TEST_CASE("parser_utils", "[misc]") SECTION("torch::Tensor") { REQUIRE_THROWS_WITH(utils::parse("1"), - Catch::Matchers::Contains("Cannot parse torch::Tensor")); + Catch::Matchers::ContainsSubstring("Cannot parse torch::Tensor")); } SECTION("TorchShape") @@ -74,7 +76,7 @@ TEST_CASE("parser_utils", "[misc]") REQUIRE(utils::parse("()") == TorchShape{}); REQUIRE_THROWS_WITH( utils::parse("1"), - Catch::Matchers::Contains("a shape must start with '(' and end with ')'")); + Catch::Matchers::ContainsSubstring("a shape must start with '(' and end with ')'")); } SECTION("bool") @@ -82,7 +84,7 @@ TEST_CASE("parser_utils", "[misc]") REQUIRE(utils::parse("true")); REQUIRE(!utils::parse("false")); REQUIRE_THROWS_WITH(utils::parse("off"), - Catch::Matchers::Contains("Failed to parse boolean value")); + Catch::Matchers::ContainsSubstring("Failed to parse boolean value")); } } } diff --git a/tests/unit/misc/test_utils.cxx b/tests/unit/misc/test_utils.cxx index ef2d77ba97..b36bbf1196 100644 --- a/tests/unit/misc/test_utils.cxx +++ b/tests/unit/misc/test_utils.cxx @@ -22,7 +22,7 @@ // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN // THE SOFTWARE. -#include +#include #include "neml2/misc/utils.h" #include "neml2/tensors/BatchTensor.h" diff --git a/tests/unit/models/test_ParameterStore.cxx b/tests/unit/models/test_ParameterStore.cxx index 5dbd364c44..b9aa31fd30 100644 --- a/tests/unit/models/test_ParameterStore.cxx +++ b/tests/unit/models/test_ParameterStore.cxx @@ -22,7 +22,8 @@ // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN // THE SOFTWARE. -#include +#include +#include #include "utils.h" #include "neml2/models/Model.h" @@ -89,9 +90,10 @@ TEST_CASE("ParameterStore", "[models]") { E.requires_grad_(true); model.value(); - REQUIRE_THROWS_WITH(math::jacrev(S.value(), E), - Catch::Matchers::Contains("The batch shape of the parameter must be the " - "same as the batch shape of the output")); + REQUIRE_THROWS_WITH( + math::jacrev(S.value(), E), + Catch::Matchers::ContainsSubstring("The batch shape of the parameter must be the " + "same as the batch shape of the output")); } SECTION("Jacobians are correct") diff --git a/tests/unit/models/test_models.cxx b/tests/unit/models/test_models.cxx index 86ec8ceee1..a7809b1472 100644 --- a/tests/unit/models/test_models.cxx +++ b/tests/unit/models/test_models.cxx @@ -22,7 +22,7 @@ // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN // THE SOFTWARE. -#include +#include #include #include "utils.h" diff --git a/tests/unit/solvers/test_NonlinearSolvers.cxx b/tests/unit/solvers/test_NonlinearSolvers.cxx index 694fa2f44d..f578fde38e 100644 --- a/tests/unit/solvers/test_NonlinearSolvers.cxx +++ b/tests/unit/solvers/test_NonlinearSolvers.cxx @@ -22,7 +22,8 @@ // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN // THE SOFTWARE. -#include +#include +#include #include "SampleNonlinearSystems.h" #include "neml2/solvers/Newton.h" diff --git a/tests/unit/solvers/test_NonlinearSystem.cxx b/tests/unit/solvers/test_NonlinearSystem.cxx index fbf275340b..b10765954d 100644 --- a/tests/unit/solvers/test_NonlinearSystem.cxx +++ b/tests/unit/solvers/test_NonlinearSystem.cxx @@ -22,7 +22,8 @@ // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN // THE SOFTWARE. -#include +#include +#include #include "SampleNonlinearSystems.h" @@ -45,6 +46,6 @@ TEST_CASE("NonlinearSystem", "[solvers]") { system.set_solution(x0); system.init_scaling(); - REQUIRE(torch::max(torch::linalg_cond(system.Jacobian(x0))).item() == Approx(1.0)); + REQUIRE(torch::max(torch::linalg_cond(system.Jacobian(x0))).item() == Catch::Approx(1.0)); } } diff --git a/tests/unit/tensors/test_BatchTensor.cxx b/tests/unit/tensors/test_BatchTensor.cxx index f76f4d5393..b981406098 100644 --- a/tests/unit/tensors/test_BatchTensor.cxx +++ b/tests/unit/tensors/test_BatchTensor.cxx @@ -22,7 +22,7 @@ // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN // THE SOFTWARE. -#include +#include #include "neml2/tensors/BatchTensor.h" diff --git a/tests/unit/tensors/test_BatchTensorBase.cxx b/tests/unit/tensors/test_BatchTensorBase.cxx index c34db41e16..18218a2af0 100644 --- a/tests/unit/tensors/test_BatchTensorBase.cxx +++ b/tests/unit/tensors/test_BatchTensorBase.cxx @@ -22,7 +22,8 @@ // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN // THE SOFTWARE. -#include +#include +#include #include "neml2/tensors/BatchTensor.h" @@ -378,7 +379,7 @@ TEST_CASE("BatchTensorBase", "[tensors]") REQUIRE(b.storage().data_ptr() == a.storage().data_ptr()); REQUIRE(b.batch_sizes() == s); REQUIRE(b.base_sizes() == a.base_sizes()); - REQUIRE(torch::sum(a - b).item() == Approx(0)); + REQUIRE(torch::sum(a - b).item() == Catch::Approx(0)); } SECTION("base_expand") @@ -394,7 +395,7 @@ TEST_CASE("BatchTensorBase", "[tensors]") // tensors because they have different base shapes. However, they _should_ be broadcastable // based on libTorch's original broadcasting rules. So we need to interpret them as // torch::Tensors first before we can compute a - b. This is the correct behavior. - REQUIRE(torch::sum(torch::Tensor(a) - torch::Tensor(b)).item() == Approx(0)); + REQUIRE(torch::sum(torch::Tensor(a) - torch::Tensor(b)).item() == Catch::Approx(0)); } SECTION("batch_expand_as") @@ -427,7 +428,7 @@ TEST_CASE("BatchTensorBase", "[tensors]") auto b = a.batch_expand_copy(s); REQUIRE(b.batch_sizes() == s); REQUIRE(b.base_sizes() == a.base_sizes()); - REQUIRE(torch::sum(a - b).item() == Approx(0)); + REQUIRE(torch::sum(a - b).item() == Catch::Approx(0)); } SECTION("base_expand_copy") @@ -442,7 +443,7 @@ TEST_CASE("BatchTensorBase", "[tensors]") // tensors because they have different base shapes. However, they _should_ be broadcastable // based on libTorch's original broadcasting rules. So we need to interpret them as // torch::Tensors first before we can compute a - b. This is the correct behavior. - REQUIRE(torch::sum(torch::Tensor(a) - torch::Tensor(b)).item() == Approx(0)); + REQUIRE(torch::sum(torch::Tensor(a) - torch::Tensor(b)).item() == Catch::Approx(0)); } SECTION("batch_unsqueeze") diff --git a/tests/unit/tensors/test_FixedDimTensor.cxx b/tests/unit/tensors/test_FixedDimTensor.cxx index 797ead7128..cfbf1ef000 100644 --- a/tests/unit/tensors/test_FixedDimTensor.cxx +++ b/tests/unit/tensors/test_FixedDimTensor.cxx @@ -22,7 +22,8 @@ // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN // THE SOFTWARE. -#include +#include +#include #include "neml2/tensors/Scalar.h" #include "neml2/tensors/Vec.h" @@ -94,7 +95,8 @@ TEST_CASE("FixedDimTensor", "[tensors]") #ifndef NDEBUG // Calling .defined() to make sure this doesn't get optimized away... - REQUIRE_THROWS_WITH(SR2(a, Bn).defined(), Catch::Matchers::Contains("Base shape mismatch")); + REQUIRE_THROWS_WITH(SR2(a, Bn).defined(), + Catch::Matchers::ContainsSubstring("Base shape mismatch")); #endif } @@ -110,7 +112,8 @@ TEST_CASE("FixedDimTensor", "[tensors]") #ifndef NDEBUG // Calling .defined() to make sure this doesn't get optimized away... - REQUIRE_THROWS_WITH(SR2(a).defined(), Catch::Matchers::Contains("Base shape mismatch")); + REQUIRE_THROWS_WITH(SR2(a).defined(), + Catch::Matchers::ContainsSubstring("Base shape mismatch")); #endif } } diff --git a/tests/unit/tensors/test_LabeledAxis.cxx b/tests/unit/tensors/test_LabeledAxis.cxx index 100b1eda99..c06eeb9d37 100644 --- a/tests/unit/tensors/test_LabeledAxis.cxx +++ b/tests/unit/tensors/test_LabeledAxis.cxx @@ -22,7 +22,7 @@ // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN // THE SOFTWARE. -#include +#include #include "neml2/tensors/LabeledAxis.h" #include "neml2/tensors/tensors.h" diff --git a/tests/unit/tensors/test_LabeledAxisAccesor.cxx b/tests/unit/tensors/test_LabeledAxisAccesor.cxx index 8792b80e18..7c53e90764 100644 --- a/tests/unit/tensors/test_LabeledAxisAccesor.cxx +++ b/tests/unit/tensors/test_LabeledAxisAccesor.cxx @@ -22,7 +22,8 @@ // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN // THE SOFTWARE. -#include +#include +#include #include "neml2/misc/utils.h" #include "neml2/tensors/LabeledAxisAccessor.h" @@ -38,7 +39,7 @@ TEST_CASE("LabeledAxisAccessor", "[tensors]") SECTION("LabeledAxisAccessor") { REQUIRE_THROWS_WITH(LabeledAxisAccessor("a.b", "c", "d"), - Catch::Matchers::Contains("Invalid item name")); + Catch::Matchers::ContainsSubstring("Invalid item name")); } SECTION("vec") { REQUIRE(a.vec() == std::vector{"a", "b", "c"}); } diff --git a/tests/unit/tensors/test_LabeledTensor.cxx b/tests/unit/tensors/test_LabeledTensor.cxx index a0fa5a3daf..aec6e59069 100644 --- a/tests/unit/tensors/test_LabeledTensor.cxx +++ b/tests/unit/tensors/test_LabeledTensor.cxx @@ -22,7 +22,8 @@ // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN // THE SOFTWARE. -#include +#include +#include #include "neml2/tensors/LabeledTensor.h" #include "neml2/tensors/LabeledVector.h" @@ -114,21 +115,21 @@ TEST_CASE("LabeledTensor", "[tensors]") { auto A = LabeledVector::zeros(nbatch, {&info1}); A.set(BatchTensor::ones(6).batch_expand(nbatch), "first"); - REQUIRE(torch::sum(A("first")).item() == Approx(nbatch * 6)); - REQUIRE(torch::sum(A("second")).item() == Approx(0)); - REQUIRE(torch::sum(A("third")).item() == Approx(0)); + REQUIRE(torch::sum(A("first")).item() == Catch::Approx(nbatch * 6)); + REQUIRE(torch::sum(A("second")).item() == Catch::Approx(0)); + REQUIRE(torch::sum(A("third")).item() == Catch::Approx(0)); } SECTION("logically 2D LabeledTensor") { auto A = LabeledMatrix::zeros(nbatch, {&info1, &info2}); A.set(BatchTensor::ones({1, 6}).batch_expand(nbatch), "third", "second"); - REQUIRE(torch::sum(A("first", "first")).item() == Approx(0)); - REQUIRE(torch::sum(A("first", "second")).item() == Approx(0)); - REQUIRE(torch::sum(A("second", "first")).item() == Approx(0)); - REQUIRE(torch::sum(A("second", "second")).item() == Approx(0)); - REQUIRE(torch::sum(A("third", "first")).item() == Approx(0)); - REQUIRE(torch::sum(A("third", "second")).item() == Approx(nbatch * 6)); + REQUIRE(torch::sum(A("first", "first")).item() == Catch::Approx(0)); + REQUIRE(torch::sum(A("first", "second")).item() == Catch::Approx(0)); + REQUIRE(torch::sum(A("second", "first")).item() == Catch::Approx(0)); + REQUIRE(torch::sum(A("second", "second")).item() == Catch::Approx(0)); + REQUIRE(torch::sum(A("third", "first")).item() == Catch::Approx(0)); + REQUIRE(torch::sum(A("third", "second")).item() == Catch::Approx(nbatch * 6)); } } @@ -154,8 +155,8 @@ TEST_CASE("LabeledTensor", "[tensors]") // Since B is a deep copy, modifying B shouldn't affect A. B.set(BatchTensor::ones({1, 6}).batch_expand(nbatch), "third", "second"); - REQUIRE(torch::sum(A("third", "second")).item() == Approx(0)); - REQUIRE(torch::sum(B("third", "second")).item() == Approx(nbatch * 6)); + REQUIRE(torch::sum(A("third", "second")).item() == Catch::Approx(0)); + REQUIRE(torch::sum(B("third", "second")).item() == Catch::Approx(nbatch * 6)); } SECTION("slice") @@ -179,8 +180,8 @@ TEST_CASE("LabeledTensor", "[tensors]") auto A = LabeledVector::zeros(nbatch, {&info1}); A.set(2.3 * BatchTensor::ones(7).batch_expand(nbatch), "sub1"); auto B = A.slice("sub1"); - REQUIRE(torch::sum(B("first")).item() == Approx(nbatch * 6 * 2.3)); - REQUIRE(torch::sum(B("second")).item() == Approx(nbatch * 2.3)); + REQUIRE(torch::sum(B("first")).item() == Catch::Approx(nbatch * 6 * 2.3)); + REQUIRE(torch::sum(B("second")).item() == Catch::Approx(nbatch * 2.3)); } SECTION("logically 2D LabeledTensor") @@ -188,10 +189,12 @@ TEST_CASE("LabeledTensor", "[tensors]") auto A = LabeledMatrix::zeros(nbatch, {&info1, &info2}); A.set(-1.9 * BatchTensor::ones({7, 6}).batch_expand(nbatch), "sub1", "second"); auto B = A.slice(0, "sub1"); - REQUIRE(torch::sum(B("first", "first")).item() == Approx(0)); - REQUIRE(torch::sum(B("first", "second")).item() == Approx(nbatch * 6 * 6 * -1.9)); - REQUIRE(torch::sum(B("second", "first")).item() == Approx(0)); - REQUIRE(torch::sum(B("second", "second")).item() == Approx(nbatch * 1 * 6 * -1.9)); + REQUIRE(torch::sum(B("first", "first")).item() == Catch::Approx(0)); + REQUIRE(torch::sum(B("first", "second")).item() == + Catch::Approx(nbatch * 6 * 6 * -1.9)); + REQUIRE(torch::sum(B("second", "first")).item() == Catch::Approx(0)); + REQUIRE(torch::sum(B("second", "second")).item() == + Catch::Approx(nbatch * 1 * 6 * -1.9)); } } } diff --git a/tests/unit/tensors/test_Quaternion.cxx b/tests/unit/tensors/test_Quaternion.cxx index b37fd06b6f..07a647290b 100644 --- a/tests/unit/tensors/test_Quaternion.cxx +++ b/tests/unit/tensors/test_Quaternion.cxx @@ -22,7 +22,7 @@ // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN // THE SOFTWARE. -#include +#include #include "utils.h" #include "neml2/tensors/tensors.h" diff --git a/tests/unit/tensors/test_R2.cxx b/tests/unit/tensors/test_R2.cxx index 53e992be89..f57dc2377a 100644 --- a/tests/unit/tensors/test_R2.cxx +++ b/tests/unit/tensors/test_R2.cxx @@ -22,7 +22,7 @@ // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN // THE SOFTWARE. -#include +#include #include "utils.h" #include "neml2/tensors/tensors.h" diff --git a/tests/unit/tensors/test_R3.cxx b/tests/unit/tensors/test_R3.cxx index 38963ad95c..ea48c94cc8 100644 --- a/tests/unit/tensors/test_R3.cxx +++ b/tests/unit/tensors/test_R3.cxx @@ -22,7 +22,7 @@ // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN // THE SOFTWARE. -#include +#include #include "utils.h" #include "neml2/tensors/tensors.h" diff --git a/tests/unit/tensors/test_R4.cxx b/tests/unit/tensors/test_R4.cxx index 8afe3b28ea..0c2e4dec1d 100644 --- a/tests/unit/tensors/test_R4.cxx +++ b/tests/unit/tensors/test_R4.cxx @@ -22,7 +22,7 @@ // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN // THE SOFTWARE. -#include +#include #include "utils.h" #include "neml2/tensors/tensors.h" diff --git a/tests/unit/tensors/test_Rot.cxx b/tests/unit/tensors/test_Rot.cxx index 88a64beb53..379317ab1d 100644 --- a/tests/unit/tensors/test_Rot.cxx +++ b/tests/unit/tensors/test_Rot.cxx @@ -22,7 +22,7 @@ // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN // THE SOFTWARE. -#include +#include #include "utils.h" #include "neml2/tensors/tensors.h" diff --git a/tests/unit/tensors/test_SR2.cxx b/tests/unit/tensors/test_SR2.cxx index ae5d7cd15f..d4729aa6fb 100644 --- a/tests/unit/tensors/test_SR2.cxx +++ b/tests/unit/tensors/test_SR2.cxx @@ -22,7 +22,7 @@ // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN // THE SOFTWARE. -#include +#include #include "utils.h" #include "neml2/tensors/tensors.h" diff --git a/tests/unit/tensors/test_SSR4.cxx b/tests/unit/tensors/test_SSR4.cxx index 4c570094ef..69e0c57e1e 100644 --- a/tests/unit/tensors/test_SSR4.cxx +++ b/tests/unit/tensors/test_SSR4.cxx @@ -22,7 +22,7 @@ // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN // THE SOFTWARE. -#include +#include #include "utils.h" #include "neml2/tensors/tensors.h" diff --git a/tests/unit/tensors/test_Scalar.cxx b/tests/unit/tensors/test_Scalar.cxx index 33986c5ea2..5a7a640768 100644 --- a/tests/unit/tensors/test_Scalar.cxx +++ b/tests/unit/tensors/test_Scalar.cxx @@ -22,7 +22,7 @@ // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN // THE SOFTWARE. -#include +#include #include "utils.h" #include "neml2/tensors/tensors.h" diff --git a/tests/unit/tensors/test_Vec.cxx b/tests/unit/tensors/test_Vec.cxx index 35ed52bccd..b22a6b771f 100644 --- a/tests/unit/tensors/test_Vec.cxx +++ b/tests/unit/tensors/test_Vec.cxx @@ -22,7 +22,7 @@ // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN // THE SOFTWARE. -#include +#include #include "utils.h" #include "neml2/tensors/tensors.h" diff --git a/tests/unit/tensors/test_VecBase.cxx b/tests/unit/tensors/test_VecBase.cxx index 97b4d2737a..9a4028414c 100644 --- a/tests/unit/tensors/test_VecBase.cxx +++ b/tests/unit/tensors/test_VecBase.cxx @@ -22,7 +22,7 @@ // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN // THE SOFTWARE. -#include +#include #include "utils.h" #include "neml2/tensors/tensors.h" diff --git a/tests/unit/tensors/test_WR2.cxx b/tests/unit/tensors/test_WR2.cxx index d799770603..5057360db7 100644 --- a/tests/unit/tensors/test_WR2.cxx +++ b/tests/unit/tensors/test_WR2.cxx @@ -22,7 +22,7 @@ // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN // THE SOFTWARE. -#include +#include #include "utils.h" #include "neml2/tensors/tensors.h" diff --git a/tests/unit/tensors/test_WWR4.cxx b/tests/unit/tensors/test_WWR4.cxx index 6e385291c2..2c6da7221c 100644 --- a/tests/unit/tensors/test_WWR4.cxx +++ b/tests/unit/tensors/test_WWR4.cxx @@ -22,7 +22,7 @@ // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN // THE SOFTWARE. -#include +#include #include "utils.h" #include "neml2/tensors/tensors.h" diff --git a/tests/unit/tensors/test_list_tensors.cxx b/tests/unit/tensors/test_list_tensors.cxx index d30a098387..4335305558 100644 --- a/tests/unit/tensors/test_list_tensors.cxx +++ b/tests/unit/tensors/test_list_tensors.cxx @@ -22,7 +22,7 @@ // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN // THE SOFTWARE. -#include +#include #include "utils.h" #include "neml2/tensors/tensors.h" diff --git a/tests/unit/tensors/test_symmetry_operator_definitions.cxx b/tests/unit/tensors/test_symmetry_operator_definitions.cxx index 4ba89f4295..c78aa49cd2 100644 --- a/tests/unit/tensors/test_symmetry_operator_definitions.cxx +++ b/tests/unit/tensors/test_symmetry_operator_definitions.cxx @@ -22,7 +22,7 @@ // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN // THE SOFTWARE. -#include +#include #include "neml2/tensors/tensors.h" #include "neml2/tensors/Transformable.h" diff --git a/tests/unit/tensors/test_vector_transform.cxx b/tests/unit/tensors/test_vector_transform.cxx index f30e1847db..c3f92eebc6 100644 --- a/tests/unit/tensors/test_vector_transform.cxx +++ b/tests/unit/tensors/test_vector_transform.cxx @@ -22,7 +22,7 @@ // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN // THE SOFTWARE. -#include +#include #include "neml2/tensors/Transformable.h" diff --git a/tests/unit/tensors/user_tensors/test_EmptyBatchTensor.cxx b/tests/unit/tensors/user_tensors/test_EmptyBatchTensor.cxx index 21b3a25f7f..105ab015be 100644 --- a/tests/unit/tensors/user_tensors/test_EmptyBatchTensor.cxx +++ b/tests/unit/tensors/user_tensors/test_EmptyBatchTensor.cxx @@ -22,7 +22,7 @@ // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN // THE SOFTWARE. -#include +#include #include "utils.h" #include "neml2/tensors/user_tensors/EmptyBatchTensor.h" diff --git a/tests/unit/tensors/user_tensors/test_EmptyFixedDimTensor.cxx b/tests/unit/tensors/user_tensors/test_EmptyFixedDimTensor.cxx index 54c28dd82f..1420edcf62 100644 --- a/tests/unit/tensors/user_tensors/test_EmptyFixedDimTensor.cxx +++ b/tests/unit/tensors/user_tensors/test_EmptyFixedDimTensor.cxx @@ -22,7 +22,7 @@ // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN // THE SOFTWARE. -#include +#include #include "utils.h" #include "neml2/tensors/user_tensors/EmptyFixedDimTensor.h" diff --git a/tests/unit/tensors/user_tensors/test_Fill3DVec.cxx b/tests/unit/tensors/user_tensors/test_Fill3DVec.cxx index 01cae308f3..b8e1581b09 100644 --- a/tests/unit/tensors/user_tensors/test_Fill3DVec.cxx +++ b/tests/unit/tensors/user_tensors/test_Fill3DVec.cxx @@ -22,7 +22,7 @@ // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN // THE SOFTWARE. -#include +#include #include "utils.h" #include "neml2/tensors/user_tensors/Fill3DVec.h" diff --git a/tests/unit/tensors/user_tensors/test_FillR2.cxx b/tests/unit/tensors/user_tensors/test_FillR2.cxx index a1d9149c4b..97d5e7cb25 100644 --- a/tests/unit/tensors/user_tensors/test_FillR2.cxx +++ b/tests/unit/tensors/user_tensors/test_FillR2.cxx @@ -22,7 +22,7 @@ // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN // THE SOFTWARE. -#include +#include #include "utils.h" #include "neml2/tensors/user_tensors/FillR2.h" diff --git a/tests/unit/tensors/user_tensors/test_FillSR2.cxx b/tests/unit/tensors/user_tensors/test_FillSR2.cxx index 3f113758ba..d6a1378ad5 100644 --- a/tests/unit/tensors/user_tensors/test_FillSR2.cxx +++ b/tests/unit/tensors/user_tensors/test_FillSR2.cxx @@ -22,7 +22,7 @@ // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN // THE SOFTWARE. -#include +#include #include "utils.h" #include "neml2/tensors/user_tensors/FillSR2.h" diff --git a/tests/unit/tensors/user_tensors/test_FullBatchTensor.cxx b/tests/unit/tensors/user_tensors/test_FullBatchTensor.cxx index 6792a1e22c..b27c002314 100644 --- a/tests/unit/tensors/user_tensors/test_FullBatchTensor.cxx +++ b/tests/unit/tensors/user_tensors/test_FullBatchTensor.cxx @@ -22,7 +22,7 @@ // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN // THE SOFTWARE. -#include +#include #include "utils.h" #include "neml2/tensors/user_tensors/FullBatchTensor.h" diff --git a/tests/unit/tensors/user_tensors/test_FullFixedDimTensor.cxx b/tests/unit/tensors/user_tensors/test_FullFixedDimTensor.cxx index ef7c77a9ec..113f366a23 100644 --- a/tests/unit/tensors/user_tensors/test_FullFixedDimTensor.cxx +++ b/tests/unit/tensors/user_tensors/test_FullFixedDimTensor.cxx @@ -22,7 +22,7 @@ // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN // THE SOFTWARE. -#include +#include #include "utils.h" #include "neml2/tensors/user_tensors/FullFixedDimTensor.h" diff --git a/tests/unit/tensors/user_tensors/test_IdentityBatchTensor.cxx b/tests/unit/tensors/user_tensors/test_IdentityBatchTensor.cxx index 87df9d203a..a4c5d5cf55 100644 --- a/tests/unit/tensors/user_tensors/test_IdentityBatchTensor.cxx +++ b/tests/unit/tensors/user_tensors/test_IdentityBatchTensor.cxx @@ -22,7 +22,7 @@ // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN // THE SOFTWARE. -#include +#include #include "utils.h" #include "neml2/tensors/user_tensors/IdentityBatchTensor.h" diff --git a/tests/unit/tensors/user_tensors/test_LinspaceBatchTensor.cxx b/tests/unit/tensors/user_tensors/test_LinspaceBatchTensor.cxx index b1e8732d3a..6a8a48eec8 100644 --- a/tests/unit/tensors/user_tensors/test_LinspaceBatchTensor.cxx +++ b/tests/unit/tensors/user_tensors/test_LinspaceBatchTensor.cxx @@ -22,7 +22,7 @@ // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN // THE SOFTWARE. -#include +#include #include "utils.h" #include "neml2/tensors/user_tensors/LinspaceBatchTensor.h" diff --git a/tests/unit/tensors/user_tensors/test_LinspaceFixedDimTensor.cxx b/tests/unit/tensors/user_tensors/test_LinspaceFixedDimTensor.cxx index 2d2485f4b9..795eedce15 100644 --- a/tests/unit/tensors/user_tensors/test_LinspaceFixedDimTensor.cxx +++ b/tests/unit/tensors/user_tensors/test_LinspaceFixedDimTensor.cxx @@ -22,7 +22,7 @@ // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN // THE SOFTWARE. -#include +#include #include "utils.h" #include "neml2/tensors/user_tensors/LinspaceFixedDimTensor.h" diff --git a/tests/unit/tensors/user_tensors/test_LogspaceBatchTensor.cxx b/tests/unit/tensors/user_tensors/test_LogspaceBatchTensor.cxx index 0d1c1eea4a..40d70dea55 100644 --- a/tests/unit/tensors/user_tensors/test_LogspaceBatchTensor.cxx +++ b/tests/unit/tensors/user_tensors/test_LogspaceBatchTensor.cxx @@ -22,7 +22,7 @@ // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN // THE SOFTWARE. -#include +#include #include "utils.h" #include "neml2/tensors/user_tensors/LogspaceBatchTensor.h" diff --git a/tests/unit/tensors/user_tensors/test_LogspaceFixedDimTensor.cxx b/tests/unit/tensors/user_tensors/test_LogspaceFixedDimTensor.cxx index 4f627939ae..b50232229c 100644 --- a/tests/unit/tensors/user_tensors/test_LogspaceFixedDimTensor.cxx +++ b/tests/unit/tensors/user_tensors/test_LogspaceFixedDimTensor.cxx @@ -22,7 +22,7 @@ // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN // THE SOFTWARE. -#include +#include #include "utils.h" #include "neml2/tensors/user_tensors/LogspaceFixedDimTensor.h" diff --git a/tests/unit/tensors/user_tensors/test_OnesBatchTensor.cxx b/tests/unit/tensors/user_tensors/test_OnesBatchTensor.cxx index ee7705c0f0..2a89e79785 100644 --- a/tests/unit/tensors/user_tensors/test_OnesBatchTensor.cxx +++ b/tests/unit/tensors/user_tensors/test_OnesBatchTensor.cxx @@ -22,7 +22,7 @@ // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN // THE SOFTWARE. -#include +#include #include "utils.h" #include "neml2/tensors/user_tensors/OnesBatchTensor.h" diff --git a/tests/unit/tensors/user_tensors/test_OnesFixedDimTensor.cxx b/tests/unit/tensors/user_tensors/test_OnesFixedDimTensor.cxx index dac7b95a47..e5d6c0843a 100644 --- a/tests/unit/tensors/user_tensors/test_OnesFixedDimTensor.cxx +++ b/tests/unit/tensors/user_tensors/test_OnesFixedDimTensor.cxx @@ -22,7 +22,7 @@ // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN // THE SOFTWARE. -#include +#include #include "utils.h" #include "neml2/tensors/user_tensors/OnesFixedDimTensor.h" diff --git a/tests/unit/tensors/user_tensors/test_Orientation.cxx b/tests/unit/tensors/user_tensors/test_Orientation.cxx index e1b9a164c4..717f6d6e9c 100644 --- a/tests/unit/tensors/user_tensors/test_Orientation.cxx +++ b/tests/unit/tensors/user_tensors/test_Orientation.cxx @@ -22,7 +22,7 @@ // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN // THE SOFTWARE. -#include +#include #include "utils.h" #include "neml2/tensors/user_tensors/Orientation.h" diff --git a/tests/unit/tensors/user_tensors/test_UserBatchTensor.cxx b/tests/unit/tensors/user_tensors/test_UserBatchTensor.cxx index b7effa3332..fa880b46e6 100644 --- a/tests/unit/tensors/user_tensors/test_UserBatchTensor.cxx +++ b/tests/unit/tensors/user_tensors/test_UserBatchTensor.cxx @@ -22,7 +22,8 @@ // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN // THE SOFTWARE. -#include +#include +#include #include "utils.h" #include "neml2/tensors/user_tensors/UserBatchTensor.h" @@ -70,7 +71,7 @@ TEST_CASE("UserBatchTensor", "[tensors/user_tensors]") REQUIRE_THROWS_WITH( Factory::get_object_ptr("Tensors", "a"), - Catch::Matchers::Contains("Number of values 1 must equal to either the " - "base storage size 6 or the total storage size 12")); + Catch::Matchers::ContainsSubstring("Number of values 1 must equal to either the " + "base storage size 6 or the total storage size 12")); } } diff --git a/tests/unit/tensors/user_tensors/test_UserFixedDimTensor.cxx b/tests/unit/tensors/user_tensors/test_UserFixedDimTensor.cxx index af9d433c37..2199cd0db8 100644 --- a/tests/unit/tensors/user_tensors/test_UserFixedDimTensor.cxx +++ b/tests/unit/tensors/user_tensors/test_UserFixedDimTensor.cxx @@ -22,7 +22,7 @@ // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN // THE SOFTWARE. -#include +#include #include "utils.h" #include "neml2/tensors/user_tensors/UserFixedDimTensor.h" diff --git a/tests/unit/tensors/user_tensors/test_ZerosBatchTensor.cxx b/tests/unit/tensors/user_tensors/test_ZerosBatchTensor.cxx index 6719c56d3f..c39d2de2f9 100644 --- a/tests/unit/tensors/user_tensors/test_ZerosBatchTensor.cxx +++ b/tests/unit/tensors/user_tensors/test_ZerosBatchTensor.cxx @@ -22,7 +22,7 @@ // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN // THE SOFTWARE. -#include +#include #include "utils.h" #include "neml2/tensors/user_tensors/ZerosBatchTensor.h" diff --git a/tests/unit/tensors/user_tensors/test_ZerosFixedDimTensor.cxx b/tests/unit/tensors/user_tensors/test_ZerosFixedDimTensor.cxx index 370cd2e56f..d09d8dea74 100644 --- a/tests/unit/tensors/user_tensors/test_ZerosFixedDimTensor.cxx +++ b/tests/unit/tensors/user_tensors/test_ZerosFixedDimTensor.cxx @@ -22,7 +22,7 @@ // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN // THE SOFTWARE. -#include +#include #include "utils.h" #include "neml2/tensors/user_tensors/ZerosFixedDimTensor.h" diff --git a/tests/verification/main.cxx b/tests/verification/main.cxx deleted file mode 100644 index 740a4fd06a..0000000000 --- a/tests/verification/main.cxx +++ /dev/null @@ -1,27 +0,0 @@ -// Copyright 2023, UChicago Argonne, LLC -// All Rights Reserved -// Software Name: NEML2 -- the New Engineering material Model Library, version 2 -// By: Argonne National Laboratory -// OPEN SOURCE LICENSE (MIT) -// -// Permission is hereby granted, free of charge, to any person obtaining a copy -// of this software and associated documentation files (the "Software"), to deal -// in the Software without restriction, including without limitation the rights -// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -// copies of the Software, and to permit persons to whom the Software is -// furnished to do so, subject to the following conditions: -// -// The above copyright notice and this permission notice shall be included in -// all copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -// THE SOFTWARE. - -#define CATCH_CONFIG_MAIN - -#include diff --git a/tests/verification/solid_mechanics/verification_solid_mechanics.cxx b/tests/verification/solid_mechanics/verification_solid_mechanics.cxx index 2f11e7b4e5..5c712ccf3d 100644 --- a/tests/verification/solid_mechanics/verification_solid_mechanics.cxx +++ b/tests/verification/solid_mechanics/verification_solid_mechanics.cxx @@ -22,7 +22,7 @@ // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN // THE SOFTWARE. -#include +#include #include #include "utils.h" From c641ce9712d8c56be236eca2d1bb8852d34227aa Mon Sep 17 00:00:00 2001 From: Gary Hu Date: Fri, 3 May 2024 07:23:04 -0500 Subject: [PATCH 06/55] Migrate benchmark tests from catch2 v2->v3 --- tests/benchmark/chaboche/chaboche.cxx | 1 + tests/benchmark/taylor_rolling_fcc/taylor_rolling_fcc.cxx | 1 + .../taylor_single_orientation/taylor_single_orientation.cxx | 1 + 3 files changed, 3 insertions(+) diff --git a/tests/benchmark/chaboche/chaboche.cxx b/tests/benchmark/chaboche/chaboche.cxx index b7f26b9219..ed5f2c9938 100644 --- a/tests/benchmark/chaboche/chaboche.cxx +++ b/tests/benchmark/chaboche/chaboche.cxx @@ -23,6 +23,7 @@ // THE SOFTWARE. #include +#include #include "utils.h" #include "neml2/drivers/Driver.h" diff --git a/tests/benchmark/taylor_rolling_fcc/taylor_rolling_fcc.cxx b/tests/benchmark/taylor_rolling_fcc/taylor_rolling_fcc.cxx index edd824892e..2462c388fa 100644 --- a/tests/benchmark/taylor_rolling_fcc/taylor_rolling_fcc.cxx +++ b/tests/benchmark/taylor_rolling_fcc/taylor_rolling_fcc.cxx @@ -23,6 +23,7 @@ // THE SOFTWARE. #include +#include #include "utils.h" #include "neml2/drivers/Driver.h" diff --git a/tests/benchmark/taylor_single_orientation/taylor_single_orientation.cxx b/tests/benchmark/taylor_single_orientation/taylor_single_orientation.cxx index 610f1df295..ecf36b2264 100644 --- a/tests/benchmark/taylor_single_orientation/taylor_single_orientation.cxx +++ b/tests/benchmark/taylor_single_orientation/taylor_single_orientation.cxx @@ -23,6 +23,7 @@ // THE SOFTWARE. #include +#include #include "utils.h" #include "neml2/drivers/Driver.h" From ad779e2981b317e259ec89935f3c25b66872c4d2 Mon Sep 17 00:00:00 2001 From: Gary Hu Date: Fri, 3 May 2024 08:07:42 -0500 Subject: [PATCH 07/55] Change gpertools and hit from git submodule to cmake fetchcontent --- .gitmodules | 6 ------ extern/gperftools | 1 - extern/hit/CMakeLists.txt | 28 ------------------------- extern/hit/hit | 1 - src/neml2/CMakeLists.txt | 44 ++++++++++++++++++++++++++++++++------- tests/CMakeLists.txt | 42 +++++++++++++++++++++++-------------- 6 files changed, 63 insertions(+), 59 deletions(-) delete mode 160000 extern/gperftools delete mode 100644 extern/hit/CMakeLists.txt delete mode 160000 extern/hit/hit diff --git a/.gitmodules b/.gitmodules index 4ce79193f2..e69de29bb2 100644 --- a/.gitmodules +++ b/.gitmodules @@ -1,6 +0,0 @@ -[submodule "extern/hit/hit"] - path = extern/hit/hit - url = https://github.com/idaholab/hit.git -[submodule "extern/gperftools"] - path = extern/gperftools - url = https://github.com/gperftools/gperftools.git diff --git a/extern/gperftools b/extern/gperftools deleted file mode 160000 index dffb4a2f28..0000000000 --- a/extern/gperftools +++ /dev/null @@ -1 +0,0 @@ -Subproject commit dffb4a2f284700b814e2c3aa1f9263fd14e836f8 diff --git a/extern/hit/CMakeLists.txt b/extern/hit/CMakeLists.txt deleted file mode 100644 index 8d4bc05eb4..0000000000 --- a/extern/hit/CMakeLists.txt +++ /dev/null @@ -1,28 +0,0 @@ -add_library(hit SHARED - hit/parse.cc - hit/lex.cc - hit/braceexpr.cc -) - -if(Torch_CXX11_ABI) - target_compile_definitions(hit PUBLIC _GLIBCXX_USE_CXX11_ABI=1) -else() - target_compile_definitions(hit PUBLIC _GLIBCXX_USE_CXX11_ABI=0) -endif() - -set_target_properties(hit PROPERTIES UNITY_BUILD OFF) -target_include_directories(hit PUBLIC .) -target_sources(hit - PUBLIC - FILE_SET HEADERS - FILES - hit/braceexpr.h - hit/hit.h - hit/lex.h - hit/parse.h -) -install( - TARGETS hit - FILE_SET HEADERS - COMPONENT Development -) diff --git a/extern/hit/hit b/extern/hit/hit deleted file mode 160000 index 1a05e6d78e..0000000000 --- a/extern/hit/hit +++ /dev/null @@ -1 +0,0 @@ -Subproject commit 1a05e6d78ed5080e8d9f8bebfd85630d3bd7bf10 diff --git a/src/neml2/CMakeLists.txt b/src/neml2/CMakeLists.txt index 4ed7b52f39..b791edf23a 100644 --- a/src/neml2/CMakeLists.txt +++ b/src/neml2/CMakeLists.txt @@ -41,24 +41,54 @@ target_sources(neml2 FILES ${_NEML2_HEADERS} ) +install( + TARGETS neml2 + FILE_SET HEADERS + COMPONENT Development +) # torch target_include_directories(neml2 SYSTEM PUBLIC ${Torch_INCLUDE_DIRECTORIES}) target_link_directories(neml2 PUBLIC ${Torch_LINK_DIRECTORIES}) target_link_libraries(neml2 PUBLIC ${Torch_LIBRARIES}) +# hit for parsing +FetchContent_Declare( + hit + GIT_REPOSITORY https://github.com/idaholab/hit.git + GIT_TAG 100b575af08643b5e646cac8faff6c87dd1c15a7 + SOURCE_DIR ${NEML2_BINARY_DIR}/_deps/hit/hit +) +message(STATUS "Downloading hit, this may take a few minutes.") +FetchContent_MakeAvailable(hit) + +add_library(hit SHARED + ${hit_SOURCE_DIR}/parse.cc + ${hit_SOURCE_DIR}/lex.cc + ${hit_SOURCE_DIR}/braceexpr.cc +) + if(Torch_CXX11_ABI) - target_compile_definitions(neml2 PUBLIC _GLIBCXX_USE_CXX11_ABI=1) + target_compile_definitions(hit PUBLIC _GLIBCXX_USE_CXX11_ABI=1) else() - target_compile_definitions(neml2 PUBLIC _GLIBCXX_USE_CXX11_ABI=0) + target_compile_definitions(hit PUBLIC _GLIBCXX_USE_CXX11_ABI=0) endif() -# hit for parsing -add_subdirectory("${NEML2_SOURCE_DIR}/extern/hit" "${NEML2_BINARY_DIR}/extern/hit") -target_link_libraries(neml2 PUBLIC hit) - +set_target_properties(hit PROPERTIES UNITY_BUILD OFF) +target_include_directories(hit PUBLIC ${hit_SOURCE_DIR}/..) +target_sources(hit + PUBLIC + FILE_SET HEADERS + BASE_DIRS ${hit_SOURCE_DIR}/.. + FILES + ${hit_SOURCE_DIR}/braceexpr.h + ${hit_SOURCE_DIR}/hit.h + ${hit_SOURCE_DIR}/lex.h + ${hit_SOURCE_DIR}/parse.h +) install( - TARGETS neml2 + TARGETS hit FILE_SET HEADERS COMPONENT Development ) +target_link_libraries(neml2 PUBLIC hit) diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt index 24355b1f09..4ac740d148 100644 --- a/tests/CMakeLists.txt +++ b/tests/CMakeLists.txt @@ -1,19 +1,21 @@ include(NEML2UnityGroup) -# Catch2, for testing -FetchContent_Declare( - Catch2 - GIT_REPOSITORY https://github.com/catchorg/Catch2.git - GIT_TAG b5373dadca40b7edc8570cf9470b9b1cb1934d40 # version 3.5.4 -) -message(STATUS "Downloading Catch2, this may take a few minutes.") -FetchContent_MakeAvailable(Catch2) - -# Catch2 v3 is built as a static library, so we MUST make sure the compile definitions are compatible with ours -if(Torch_CXX11_ABI) - target_compile_definitions(Catch2 PUBLIC _GLIBCXX_USE_CXX11_ABI=1) -else() - target_compile_definitions(Catch2 PUBLIC _GLIBCXX_USE_CXX11_ABI=0) +if(NEML2_UNIT OR NEML2_REGRESSION OR NEML2_VERIFICATION OR NEML2_BENCHMARK) + # Catch2, for testing + FetchContent_Declare( + Catch2 + GIT_REPOSITORY https://github.com/catchorg/Catch2.git + GIT_TAG b5373dadca40b7edc8570cf9470b9b1cb1934d40 # version 3.5.4 + ) + message(STATUS "Downloading Catch2, this may take a few minutes.") + FetchContent_MakeAvailable(Catch2) + + # Catch2 v3 is built as a static library, so we MUST make sure the compile definitions are compatible with ours + if(Torch_CXX11_ABI) + target_compile_definitions(Catch2 PUBLIC _GLIBCXX_USE_CXX11_ABI=1) + else() + target_compile_definitions(Catch2 PUBLIC _GLIBCXX_USE_CXX11_ABI=0) + endif() endif() # Test utilities @@ -171,8 +173,16 @@ endif() option(NEML2_PROFILING "Build NEML2 profiling tests" OFF) if(NEML2_PROFILING) - # gperftools for profiling - add_subdirectory(${NEML2_SOURCE_DIR}/extern/gperftools ${NEML2_BINARY_DIR}/extern/gperftools EXCLUDE_FROM_ALL) + # gperftools + FetchContent_Declare( + gperftools + GIT_REPOSITORY https://github.com/gperftools/gperftools.git + GIT_TAG gperftools-2.15 + EXCLUDE_FROM_ALL + ) + message(STATUS "Downloading gperftools, this may take a few minutes.") + FetchContent_MakeAvailable(gperftools) + file(GLOB_RECURSE PROFILING_TESTS profiling/*.cxx) add_executable(profiling_tests ${PROFILING_TESTS}) From ecc83c07fc65ab65c3677c1512ff7642b7551d46 Mon Sep 17 00:00:00 2001 From: Gary Hu Date: Fri, 3 May 2024 08:31:43 -0500 Subject: [PATCH 08/55] Change pybind11 from a python package dependency to cmake fetchcontent --- doc/CMakeLists.txt | 2 +- python/CMakeLists.txt | 19 ++++++++++--------- python/requirements.txt | 5 ----- requirements.txt | 1 - tests/CMakeLists.txt | 2 +- 5 files changed, 12 insertions(+), 17 deletions(-) delete mode 100644 python/requirements.txt diff --git a/doc/CMakeLists.txt b/doc/CMakeLists.txt index 6fc380ab4e..0c14934d67 100644 --- a/doc/CMakeLists.txt +++ b/doc/CMakeLists.txt @@ -16,7 +16,7 @@ endif() FetchContent_Declare( doxygen-awesome-css GIT_REPOSITORY https://github.com/jothepro/doxygen-awesome-css.git - GIT_TAG 5b27b3a747ca1e559fa54149762cca0bad6036fb # version 2.3.2 + GIT_TAG v2.3.2 ) message(STATUS "Downloading doxygen-awesome-css, this may take a few minutes.") FetchContent_MakeAvailable(doxygen-awesome-css) diff --git a/python/CMakeLists.txt b/python/CMakeLists.txt index 009e5fff70..f11e1dd8d2 100644 --- a/python/CMakeLists.txt +++ b/python/CMakeLists.txt @@ -1,5 +1,11 @@ -find_package(Python COMPONENTS Interpreter Development.Module) -find_package(pybind11 CONFIG REQUIRED HINTS ${Python_SITEARCH}) +# pybind11 +FetchContent_Declare( + pybind11 + GIT_REPOSITORY https://github.com/pybind/pybind11.git + GIT_TAG v2.12.0 +) +message(STATUS "Downloading pybind11, this may take a few minutes.") +FetchContent_MakeAvailable(pybind11) if(NOT Torch_PYTHON_BINDING) message(FATAL_ERROR "Could not find the libTorch Python binding") @@ -14,7 +20,7 @@ endif() # macro for defining a submodule with given source files macro(add_submodule mname msrcs) - python_add_library(${mname} MODULE ${msrcs} WITH_SOABI) + pybind11_add_module(${mname} MODULE ${msrcs}) set_target_properties(${mname} PROPERTIES LIBRARY_OUTPUT_DIRECTORY neml2) target_include_directories(${mname} PUBLIC ${NEML2_SOURCE_DIR}) target_link_libraries(${mname} PRIVATE pybind11::headers) @@ -35,9 +41,4 @@ add_submodule_dir(models) add_submodule(math neml2/misc/math.cxx) # Install python artifacts -install(DIRECTORY neml2/ - DESTINATION . - FILES_MATCHING - PATTERN "*.py" - PATTERN "*.pyi" -) +install(FILES neml2/__init__.py DESTINATION .) diff --git a/python/requirements.txt b/python/requirements.txt deleted file mode 100644 index d9cd539b30..0000000000 --- a/python/requirements.txt +++ /dev/null @@ -1,5 +0,0 @@ -# -##### pip requirements ###### -# -pybind11 -pybind11-stubgen diff --git a/requirements.txt b/requirements.txt index 0e6322036e..6b9fb504cd 100644 --- a/requirements.txt +++ b/requirements.txt @@ -3,5 +3,4 @@ # -r doc/requirements.txt -r scripts/requirements.txt --r python/requirements.txt -r tests/requirements.txt diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt index 4ac740d148..d70c410456 100644 --- a/tests/CMakeLists.txt +++ b/tests/CMakeLists.txt @@ -5,7 +5,7 @@ if(NEML2_UNIT OR NEML2_REGRESSION OR NEML2_VERIFICATION OR NEML2_BENCHMARK) FetchContent_Declare( Catch2 GIT_REPOSITORY https://github.com/catchorg/Catch2.git - GIT_TAG b5373dadca40b7edc8570cf9470b9b1cb1934d40 # version 3.5.4 + GIT_TAG v3.5.4 ) message(STATUS "Downloading Catch2, this may take a few minutes.") FetchContent_MakeAvailable(Catch2) From d0b2c484a4972d31f70cb5cd0e4b847a651b1e54 Mon Sep 17 00:00:00 2001 From: Gary Hu Date: Fri, 3 May 2024 08:34:24 -0500 Subject: [PATCH 09/55] requirements.txt no longer needed before python build --- .github/workflows/python.yml | 2 -- pyproject.toml | 2 +- 2 files changed, 1 insertion(+), 3 deletions(-) diff --git a/.github/workflows/python.yml b/.github/workflows/python.yml index cea81d2ba4..5a738ae474 100644 --- a/.github/workflows/python.yml +++ b/.github/workflows/python.yml @@ -49,8 +49,6 @@ jobs: - uses: actions/setup-python@v5 with: python-version: "3.8" - - name: Install Python dependencies - run: pip install -r requirements.txt - name: Install PyTorch run: pip install torch==2.2.2 - run: | diff --git a/pyproject.toml b/pyproject.toml index f3f1a6e72a..4a68a473eb 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,5 +1,5 @@ [build-system] -requires = ["scikit-build-core>=0.3.3", "pybind11"] +requires = ["scikit-build-core>=0.3.3"] build-backend = "scikit_build_core.build" [project] From d7da6a0bae8d462d038bb0ca07a905032f02794f Mon Sep 17 00:00:00 2001 From: Gary Hu Date: Fri, 3 May 2024 08:37:19 -0500 Subject: [PATCH 10/55] minor installation guide tweak --- doc/content/_01_install.md | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/doc/content/_01_install.md b/doc/content/_01_install.md index 57acce6db5..e5a2ab8385 100644 --- a/doc/content/_01_install.md +++ b/doc/content/_01_install.md @@ -17,7 +17,8 @@ Compiling the NEML2 core library requires Other PyTorch releases with a few minor versions around are likely to be compatible. In the PyTorch official download page, several download options are provided: conda, pip, libTorch, and source distribution. - **Recommended** If you choose to download PyTorch using conda or pip, the NEML2 CMake script can automatically detect and use the PyTorch installation. - If you choose to download libTorch or build PyTorch from source, you will need to set `LIBTORCH_DIR` to be the location of libTorch when using CMake to configure NEML2. -- If no PyTorch installation can be detected and `LIBTORCH_DIR` is not set at configure time, the NEML2 CMake script will automatically download and use the libTorch obtained from the official website. Note, however, that this method only works on Linux and Mac systems. + +If no PyTorch installation can be detected and `LIBTORCH_DIR` is not set at configure time, the NEML2 CMake script will automatically download and use the libTorch obtained from the official website. Note, however, that this method only works on Linux and Mac systems. > The libTorch distributions from the official website come with two flavors: "Pre-cxx11 ABI" and "cxx11 ABI". Both variants are supported by NEML2. If you are unsure, we recommend the one with "cxx11 ABI". @@ -31,12 +32,10 @@ Other PyTorch releases with a few minor versions around are likely to be compati - [Doxygen](https://github.com/doxygen/doxygen) for building the documentation. - [Doxygen Awesome](https://github.com/jothepro/doxygen-awesome-css) the documentation theme. - Python packages - - PyYAML + - pytest - pandas - matplotlib - - pybind11 - - pybind11-stubgen - - pytest + - PyYAML ## Build, Test, and Install From cf31a1e6a7e20f990d8da5d940c2cd7f0ca0f0a5 Mon Sep 17 00:00:00 2001 From: Gary Hu Date: Fri, 3 May 2024 09:07:15 -0500 Subject: [PATCH 11/55] There is a gperftool bug after 2.13 --- tests/CMakeLists.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt index d70c410456..5699cd535b 100644 --- a/tests/CMakeLists.txt +++ b/tests/CMakeLists.txt @@ -177,7 +177,7 @@ if(NEML2_PROFILING) FetchContent_Declare( gperftools GIT_REPOSITORY https://github.com/gperftools/gperftools.git - GIT_TAG gperftools-2.15 + GIT_TAG gperftools-2.13 EXCLUDE_FROM_ALL ) message(STATUS "Downloading gperftools, this may take a few minutes.") From fc0af51af2a90e0dc9eb3fab5d74f36085eced8d Mon Sep 17 00:00:00 2001 From: Gary Hu Date: Fri, 3 May 2024 09:38:35 -0500 Subject: [PATCH 12/55] But does it even pull those unittests? --- .github/workflows/python.yml | 2 ++ tests/CMakeLists.txt | 6 +++--- 2 files changed, 5 insertions(+), 3 deletions(-) diff --git a/.github/workflows/python.yml b/.github/workflows/python.yml index 5a738ae474..304fccd68c 100644 --- a/.github/workflows/python.yml +++ b/.github/workflows/python.yml @@ -49,6 +49,8 @@ jobs: - uses: actions/setup-python@v5 with: python-version: "3.8" + - name: Install Python dependencies + run: pip install -r tests/requirements.txt - name: Install PyTorch run: pip install torch==2.2.2 - run: | diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt index 5699cd535b..7894cf07f6 100644 --- a/tests/CMakeLists.txt +++ b/tests/CMakeLists.txt @@ -177,11 +177,11 @@ if(NEML2_PROFILING) FetchContent_Declare( gperftools GIT_REPOSITORY https://github.com/gperftools/gperftools.git - GIT_TAG gperftools-2.13 - EXCLUDE_FROM_ALL + GIT_TAG gperftools-2.15 ) message(STATUS "Downloading gperftools, this may take a few minutes.") - FetchContent_MakeAvailable(gperftools) + FetchContent_Populate(gperftools) + add_subdirectory(${gperftools_SOURCE_DIR} ${gperftools_BINARY_DIR} EXCLUDE_FROM_ALL) file(GLOB_RECURSE PROFILING_TESTS profiling/*.cxx) add_executable(profiling_tests ${PROFILING_TESTS}) From dcbb6600d2e15af9c69ee4f2116d6f37676dd1e0 Mon Sep 17 00:00:00 2001 From: Gary Hu Date: Fri, 3 May 2024 18:12:42 -0500 Subject: [PATCH 13/55] Add header permalink; Add physics documentation --- doc/config/DoxygenLayout.xml | 1 + doc/config/HTML.in | 2 + doc/config/header.html | 93 ++++++++++++++++++++++++++++++++++++ doc/content/_05_physics.md | 3 ++ scripts/syntax_to_md.py | 6 ++- 5 files changed, 103 insertions(+), 2 deletions(-) create mode 100644 doc/config/header.html create mode 100644 doc/content/_05_physics.md diff --git a/doc/config/DoxygenLayout.xml b/doc/config/DoxygenLayout.xml index b81a9b1e0d..3b9787bebc 100644 --- a/doc/config/DoxygenLayout.xml +++ b/doc/config/DoxygenLayout.xml @@ -6,6 +6,7 @@ + diff --git a/doc/config/HTML.in b/doc/config/HTML.in index eac7f0bb17..333c0c2575 100644 --- a/doc/config/HTML.in +++ b/doc/config/HTML.in @@ -5,8 +5,10 @@ GENERATE_HTML = YES DISABLE_INDEX = NO FULL_SIDEBAR = NO HTML_OUTPUT = html +HTML_HEADER = ${NEML2_SOURCE_DIR}/doc/config/header.html HTML_EXTRA_STYLESHEET = ${doxygen-awesome-css_SOURCE_DIR}/doxygen-awesome.css\ ${doxygen-awesome-css_SOURCE_DIR}/doxygen-awesome-sidebar-only.css +HTML_EXTRA_FILES = ${doxygen-awesome-css_SOURCE_DIR}/doxygen-awesome-paragraph-link.js HTML_COLORSTYLE = LIGHT GENERATE_TREEVIEW = YES USE_MATHJAX = YES diff --git a/doc/config/header.html b/doc/config/header.html new file mode 100644 index 0000000000..60cd7b045d --- /dev/null +++ b/doc/config/header.html @@ -0,0 +1,93 @@ + + + + + + + + + + + $projectname: $title + + $title + + + + + + + + + + + + + + + $treeview + $search + $mathjax + $darkmode + + $extrastylesheet + + + + + + + +
+ + + +
+ + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+
$projectname $projectnumber +
+ +
$projectbrief
+
+
$projectbrief
+
$searchbox
$searchbox
+
+ + diff --git a/doc/content/_05_physics.md b/doc/content/_05_physics.md new file mode 100644 index 0000000000..bee2cd0742 --- /dev/null +++ b/doc/content/_05_physics.md @@ -0,0 +1,3 @@ +# Physics Documentation {#physics} + +[TOC] diff --git a/scripts/syntax_to_md.py b/scripts/syntax_to_md.py index f33c37d7e2..419cbcfd49 100755 --- a/scripts/syntax_to_md.py +++ b/scripts/syntax_to_md.py @@ -51,7 +51,7 @@ def postprocess(value, type): def get_sections(syntax): sections = [params["section"] for type, params in syntax.items()] - return set(sections) + return list(dict.fromkeys(sections)) if __name__ == "__main__": @@ -99,4 +99,6 @@ def get_sections(syntax): if param_value: stream.write(" - Default: {}\n".format(param_value)) stream.write("\n") - stream.write("Details: [{}](@ref {})\n\n".format(input_type, type)) + stream.write( + "Detailed Doxygen documentation [link](@ref {})\n\n".format(type) + ) From 29161007f274a35577b823f19b7c52a941fb517d Mon Sep 17 00:00:00 2001 From: Gary Hu Date: Mon, 6 May 2024 06:40:52 -0500 Subject: [PATCH 14/55] split systems docs --- doc/config/DoxygenLayout.xml | 12 +- doc/content/_01_install.md | 26 + doc/content/_02_01_tensor.md | 195 +++++++ doc/content/_02_02_data.md | 3 + doc/content/_02_03_model.md | 106 ++++ doc/content/_02_04_solver.md | 3 + doc/content/_02_05_driver.md | 3 + doc/content/_02_system.md | 11 + doc/content/_02_user.md | 3 - doc/content/_03_dev.md | 533 ------------------ .../{_05_physics.md => _03_physics.md} | 0 doc/content/_04_dev.md | 244 ++++++++ doc/content/_04_system.md | 3 - 13 files changed, 599 insertions(+), 543 deletions(-) create mode 100644 doc/content/_02_01_tensor.md create mode 100644 doc/content/_02_02_data.md create mode 100644 doc/content/_02_03_model.md create mode 100644 doc/content/_02_04_solver.md create mode 100644 doc/content/_02_05_driver.md create mode 100644 doc/content/_02_system.md delete mode 100644 doc/content/_02_user.md delete mode 100644 doc/content/_03_dev.md rename doc/content/{_05_physics.md => _03_physics.md} (100%) create mode 100644 doc/content/_04_dev.md delete mode 100644 doc/content/_04_system.md diff --git a/doc/config/DoxygenLayout.xml b/doc/config/DoxygenLayout.xml index 3b9787bebc..23c1fecaf0 100644 --- a/doc/config/DoxygenLayout.xml +++ b/doc/config/DoxygenLayout.xml @@ -3,11 +3,15 @@ - - - + + + + + + + - + diff --git a/doc/content/_01_install.md b/doc/content/_01_install.md index e5a2ab8385..b6e3a6bd69 100644 --- a/doc/content/_01_install.md +++ b/doc/content/_01_install.md @@ -98,3 +98,29 @@ Commonly used configuration options are summarized below. Default options are un | NEML2_PROFILING | ON, OFF | Create the profiling executable target | | NEML2_DOC | ON, OFF | Create the documentation target | | NEML2_PYBIND | ON, OFF | Create the Python bindings target | + +## CMake integration {#cmake-integration} + +Integrating NEML2 into a project that already uses CMake is fairly straightforward. The following CMakeLists.txt snippet links NEML2 into the target executable called `foo`: + +``` +add_subdirectory(neml2) + +add_executable(foo main.cxx) +target_link_libraries(foo neml2) +``` + +The above snippet assumes NEML2 is checked out to the directory neml2, i.e., as a git submodule. +Alternatively, you may use CMake's `FetchContent` module to integrate NEML2 into your project: + +``` +FetchContent_Declare( + neml2 + GIT_REPOSITORY https://github.com/reverendbedford/neml2.git + GIT_TAG v1.4.0 +) +FetchContent_MakeAvailable(neml2) + +add_executable(foo main.cxx) +target_link_libraries(foo neml2) +``` diff --git a/doc/content/_02_01_tensor.md b/doc/content/_02_01_tensor.md new file mode 100644 index 0000000000..7ce9e46726 --- /dev/null +++ b/doc/content/_02_01_tensor.md @@ -0,0 +1,195 @@ +# Tensor {#tensor} + +[TOC] + +## Tensor types {#tensor-types} + +Currently, PyTorch is the only supported tensor backend in NEML2. Therefore, all tensor types in NEML2 directly inherit from `torch::Tensor`. In the future, support for other tensor backends may be added, but the public-facing interfaces will remain largely the same. + +### Dynamically shaped tensor {#dynamically-shaped-tensor} + +[BatchTensor](@ref neml2::BatchTensor) is a general-purpose *dynamically shaped* tensor type for batched tensors. With a view towards vectorization, the same set of operations can be "simultaneously" applied to a "batch" of (logically the same) tensors. To provide a unified user interface for dealing with such batched operation, NEML2 assumes that the *first* \f$N\f$ dimensions of a tensor are batched dimensions, and the following dimensions are the base (logical) dimensions. + +> Unlike PyTorch, NEML2 explicitly distinguishes between batch dimensions and base (logical) dimensions. + +A `BatchTensor` can be created using +```cpp +BatchTensor A(torch::rand({1, 1, 5, 2}), 2); +``` +where `A` is a tensor with 2 batch dimensions. The batch sizes of `A` is `(1, 1)`: +```cpp +auto batch_sz = A.batch_sizes(); +neml2_assert(batch_sz == {1, 1}); +``` +and the base (logical) sizes of `A` is `(5, 2)`: +```cpp +auto base_sz = A.base_sizes(); +neml2_assert(batch_sz == {5, 2}); +``` + +### Statically shaped tensors {#statically-shaped-tensor} + +[FixedDimTensor](@ref neml2::FixedDimTensor) is the parent class for all the tensor types with a *fixed* base shape. It is templated on the base shape of the tensor. NEML2 offers a rich collection of primitive tensor types inherited from `FixedDimTensor`. Currently implemented primitive tensor types are summarized below + +| Tensor type | Base shape | Description | +| :------------------------------------- | :---------------- | :--------------------------------------------------------------- | +| [Scalar](@ref neml2::Scalar) | \f$()\f$ | Rank-0 tensor, i.e. scalar | +| [Vec](@ref neml2::Vec) | \f$(3)\f$ | Rank-1 tensor, i.e. vector | +| [R2](@ref neml2::R2) | \f$(3,3)\f$ | Rank-2 tensor | +| [SR2](@ref neml2::SR2) | \f$(6)\f$ | Symmetric rank-2 tensor | +| [WR2](@ref neml2::WR2) | \f$(3)\f$ | Skew-symmetric rank-2 tensor | +| [R3](@ref neml2::R3) | \f$(3,3,3)\f$ | Rank-3 tensor | +| [SFR3](@ref neml2::SFR3) | \f$(6,3)\f$ | Rank-3 tensor with symmetry on base dimensions 0 and 1 | +| [R4](@ref neml2::R4) | \f$(3,3,3,3)\f$ | Rank-4 tensor | +| [SSR4](@ref neml2::SSR4) | \f$(6,6)\f$ | Rank-4 tensor with minor symmetry | +| [R5](@ref neml2::R5) | \f$(3,3,3,3,3)\f$ | Rank-5 tensor | +| [SSFR5](@ref neml2::SSFR5) | \f$(6,6,3)\f$ | Rank-5 tensor with minor symmetry on base dimensions 0-3 | +| [Rot](@ref neml2::Rot) | \f$(3)\f$ | Rotation tensor represented in the Rodrigues form | +| [Quarternion](@ref neml2::Quarternion) | \f$(4)\f$ | Quarternion | +| [MillerIndex](@ref neml2::MillerIndex) | \f$(3)\f$ | Crystal direction or lattice plane represented as Miller indices | + +Furthermore, all primitive tensor types can be "registered" as variables on a `LabeledAxis`, which will be discussed in the following section on [labeled view](@ref tensor-labeling). + +## Working with tensors {#working-with-tensors} + +### Tensor creation {#tensor-creation} + +A factory tensor creation function produces a new tensor. All factory functions adhere to the same schema: +```cpp +::(, const torch::TensorOptions & options); +``` +where `` is the class name of the primitive tensor type listed above, and `` is the name of the factory function which produces the new tensor. `` are any required or optional arguments a particular factory function accepts. Refer to each tensor type's class documentation for the concrete signature. The last argument `const torch::TensorOptions & options` configures the data type, device, layout and other "meta" properties of the produced tensor. The commonly used meta properties are +- `dtype`: the data type of the elements stored in the tensor. Available options are `kUInt8`, `kInt8`, `kInt16`, `kInt32`, `kInt64`, `kFloat32`, and `kFloat64`. +- `layout`: the striding of the tensor. Available options are `kStrided` (dense) and `kSparse`. +- `device`: the compute device where the tensor will be allocated. Available options are `kCPU` and `kCUDA`. +- `requires_grad`: whether the tensor is part of a function graph used by automatic differentiation to track functional relationship. Available options are `true` and `false`. + +For example, the following code +```cpp +auto a = SR2::zeros({5, 3}, + torch::TensorOptions() + .device(torch::kCPU) + .layout(torch::kStrided) + .dtype(torch::kFloat32)); +``` +creates a statically (base) shaped, dense, single precision tensor of type `SR2` filled with zeros, with batch shape \f$(5, 3)\f$, allocated on the CPU. + +### Tensor broadcasting {#tensor-broadcasting} + +Quoting Numpy's definition of broadcasting: + +> The term broadcasting describes how NumPy treats arrays with different shapes during arithmetic operations. Subject to certain constraints, the smaller array is “broadcast” across the larger array so that they have compatible shapes. + +NEML2's broadcasting semantics is largely the same as those of Numpy and PyTorch. However, since NEML2 explicitly distinguishes between batch and base dimensions, the broadcasting semantics must also be extended. Two NEML2 tensors are said to be _batch-broadcastable_ if iterating backward from the last batch dimension, one of the following is satisfied: +1. Both tensors have the same size on the dimension; +2. One tensor has size 1 on the dimension; +3. The dimension does not exist in one tensor. + +_Base-broadcastable_ follows a similar definition. Most binary operators on dynamically shaped tensors, i.e., those of type `BatchTensor`, require the operands to be both batch- _and_ base-broadcastable. On the other hand, most binary operators on statically base shaped tensors, i.e., those of pritimitive tensor types, only require the operands to be batch-broadcastable. + +### Tensor indexing {#tensor-indexing} + +In defining the forward operator of a material model, many logically different tensors representing inputs, outputs, residuals, and Jacobians have to be created, copied, and destroyed on the fly. These operations occupy a significant amount of computing time, especially on GPUs. + +To address this challenge, NEML2 creates *views*, instead of copies, of tensors whenever possible. As its name suggests, the view of a tensor is a possibly different interpretation of the underlying data. Quoting the PyTorch documentation: + +> For a tensor to be viewed, the new view size must be compatible with its original size and stride, i.e., each new view dimension must either be a subspace of an original dimension, or only span across original dimensions \f$d, d+1, ..., d+k\f$ that satisfy the following contiguity-like condition that \f$\forall i = d,...,d+k-1\f$, +> \f[ +> \text{stride}[i] = \text{stride}[i+1] \times \text{size}[i+1] +> \f] +> Otherwise, it will not be possible to view self tensor as shape without copying it. + +In NEML2, use [base_index](@ref neml2::BatchTensorBase::base_index) for indexing the base dimensions and [batch_index](@ref neml2::BatchTensorBase::batch_index) for indexing the batch dimensions: +```cpp +using namespace torch::indexing; +BatchTensor A(torch::tensor({{2, 3, 4}, {-1, -2, 3}, {6, 9, 7}}), 1); +// A = [[ 2 3 4] +// [ -1 -2 3] +// [ 6 9 7]] +BatchTensor B = A.batch_index({Slice(0, 2)}); +// B = [[ 2 3 4] +// [ -1 -2 3]] +BatchTensor C = A.base_index({Slice(1, 3)}); +// C = [[ 3 4] +// [ -2 3] +// [ 9 7]] +``` +To modify the content of a tensor, use [base_index_put](@ref neml2::BatchTensorBase::base_index_put) or [batch_index_put](@ref neml2::BatchTensorBase::batch_index_put): +```cpp +A.base_index_put({Slice(1, 3)}, torch::ones({3, 2})); +// A = [[ 2 1 1] +// [ -1 1 1] +// [ 6 1 1]] +A.batch_index_put({Slice(0, 2)}, torch::zeros({2, 3})); +// A = [[ 0 0 0] +// [ 0 0 0] +// [ 6 1 1]] +``` +A detailed explanation on tensor indexing APIs is available as part of the official [PyTorch documentation](https://pytorch.org/cppdocs/notes/tensor_indexing.html). + +### Tensor labeling {#tensor-labeling} + +In the context of material modeling, oftentimes views of tensors have practical/physical meanings. For example, given a logically 1D tensor with base size 9, its underlying data in an arbitrary batch may look like +``` +equivalent plastic strain 2.1 + cauchy stress -2.1 + 0 + 1.3 + -1.1 + 2.5 + 2.5 + temperature 102.9 + time 3.6 +``` +where component 0 stores the scalar-valued equivalent plastic strain, components 1-6 store the tensor-valued cauchy stress (we use the Mandel notation for symmetric second order tensors), component 7 stores the scalar-valued temperature, and component 8 stores the scalar-valued time. + +The string indicating the physical meaning of the view, e.g., "cauchy stress", is called a "label", and the view of the tensor indexed by a label is called a "labeled view", i.e., +``` + cauchy stress -2.1 + 0 + 1.3 + -1.1 + 2.5 + 2.5 +``` + +NEML2 provides a data structure named [LabeledAxis](@ref neml2::LabeledAxis) to facilitate the creation and modification of labels, and a data structure named [LabeledTensor](@ref neml2::LabeledTensor) to facilitate the creation and modification of labeled views. + +The [LabeledAxis](@ref neml2::LabeledAxis) contains all information regarding how an axis of a `LabeledTensor` is labeled. The following naming convention is used: +- Item: A labelable slice of data +- Variable: An item that is also of a [NEML2 primitive tensor type](@ref tensor-types) +- Sub-axis: An item of type `LabeledAxis` + +So yes, an axis can be labeled recursively, e.g., + +``` + 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 +/// |-----------| |-----| | | | | | | +/// a b | | | | | | +/// |-------------------| |--------------| |--| |--| +/// sub a b c +``` +The above example represents an axis of size 15. This axis has 4 items: `a`, `b`, `c`, and `sub`. +- "a" is a variable of storage size 6 (possibly of type `SR2`). +- "b" is a variable of type `Scalar`. +- "c" is a variable of type `Scalar`. +- "sub" is a sub-axis of type `LabeledAxis`. "sub" by itself represents an axis of size 7, containing 2 items: + - "a" is a variable of storage size 6. + - "b" is a variable of type `Scalar`. + +Duplicate labels are *not* allowed on the same level of the axis, e.g. "a", "b", "c", and "sub" share the same level and so must be different. However, items on different levels of an axis can share the same label, e.g., "a" on the sub-axis "sub" has the same label as "a" on the main axis. In NEML2 convention, item names are always fully qualified, and a sub-axis is prefixed with a left slash, e.g. item "b" on the sub-axis "sub" can be denoted as "sub/b" on the main axis. + +> A label cannot contain: white spaces, quotes, left slash (`/`), or new line. +> +> Due to performance considerations, a `LabeledAxis` can only be modified, e.g., adding/removing variables and sub-axis, at the time a model is constructed. After the model construction phase, the `LabeledAxis` associated with that model can no longer be modified over the entire course of the simulation. + +Refer to the documentation for a complete list of APIs for creating and modifying a [LabeledAxis](@ref neml2::LabeledAxis). + +[LabeledTensor](@ref neml2::LabeledTensor) is the primary data structure in NEML2 for working with labeled tensor views. Each `LabeledTensor` consists of one `BatchTensor` and one or more `LabeledAxis`s. The `LabeledTensor` is templated on the base dimension \f$D\f$. [LabeledVector](@ref neml2::LabeledVector) and [LabeledMatrix](@ref neml2::LabeledMatrix) are the two most widely used data structures in NEML2. + +`LabeledTensor` handles the creation, modification, and accessing of labeled tensors. Recall that all primitive data types in a labeled tensor are flattened, e.g., a symmetric fourth order tensor of type `SSR4` with batch size `(5)` and base size `(6, 6)` are flattened to have base size `(36)` in the labeled tensor. The documentation provides a complete list of APIs. The commonly used methods are +- [operator()](@ref neml2::LabeledTensor::operator()()) for retrieving a labeled view into the raw (flattened) data without reshaping +- [get](@ref neml2::LabeledTensor::get) for retrieving a labeled view and reshaping it to the correct shape +- [set](@ref neml2::LabeledTensor::set) for setting values for a labeled view +- [slice](@ref neml2::LabeledTensor::slice) for slicing a sub-axis along a specific base dimension +- [block](@ref neml2::LabeledTensor::block) for sub-indexing the `LabeledTensor` with \f$D\f$ sub-axis names diff --git a/doc/content/_02_02_data.md b/doc/content/_02_02_data.md new file mode 100644 index 0000000000..7b499cfa1d --- /dev/null +++ b/doc/content/_02_02_data.md @@ -0,0 +1,3 @@ +# Data {#data} + +[TOC] diff --git a/doc/content/_02_03_model.md b/doc/content/_02_03_model.md new file mode 100644 index 0000000000..90ab88e26f --- /dev/null +++ b/doc/content/_02_03_model.md @@ -0,0 +1,106 @@ +# Model {#model} + +[TOC] + +## Model definition {#model-definition} + +A NEML2 model is a function (in the context of mathematics) +\f[ + f: \mathbb{R}^m \to \mathbb{R}^n +\f] +mapping from the input space \f$\mathbb{R}^m\f$ of dimension \f$m\f$ to the output space \f$\mathbb{R}^n\f$ of dimension \f$n\f$. \f$\left[ \cdot \right]\f$ be the flatten-concatenation operator, the input vector is the concatenation of \f$p\f$ flattened variables, i.e., +\f[ + x = \left[ x_i \right]_{i=1}^p \in \mathbb{R}^m, \quad \sum_{i=1}^p \lvert x_i \rvert = m, +\f] +where \f$\lvert x \rvert\f$ denotes the modulus of flattened variable \f$x\f$. Similarly, the output vector is the concatenation of \f$q\f$ flattened variables, i.e., +\f[ + y = \left[ y_i \right]_{i=1}^q \in \mathbb{R}^n, \quad \sum_{i=1}^q \lvert y_i \rvert = n. +\f] + +Translating the above mathematical definition into NEML2 is straightforward. +- A model following this definition derives from [Model](@ref neml2::Model). +- [declare_input_variable](@ref neml2::Model::declare_input_variable) declares an input variable \f$x_i\f$ in the input space \f$\mathbb{R}^m\f$. +- [declare_output_variable](@ref neml2::Model::declare_output_variable) declares an output variable \f$y_i\f$ in the output space \f$\mathbb{R}^n\f$. +- [set_value](@ref neml2::Model::set_value) is a method defining the forward operator \f$f\f$ itself. + +Both `declare_input_variable` and `declare_output_variable` are templated on the variable type -- recall that only a variable of the NEML2 primitive tensor type can be registered. Furthermore, both methods return a `Variable &` used for retrieving and setting the variable value inside the forward operator, i.e. `set_value`. Note that the reference returned by `declare_input_variable` is writable, while the reference returned by `declare_output_variable` is read-only. + +## Model composition {#model-composition} + +Quoting [Wikipedia](https://en.wikipedia.org/wiki/Function_composition): +> In mathematics, function composition is an operation \f$\circ\f$ that takes two functions \f$f\f$ and \f$g\f$, and produces a function \f$h = g \circ f\f$ such that \f$h(x) = g(f(x))\f$. + +Since NEML2 `Model` is a function (in the context of mathematics) at its core, it should be possible, in theory, to compose different NEML2 `Model`s into a new NEML2 `Model`. The [ComposedModel](@ref neml2::ComposedModel) is precisely for that purpose. + +Similar to the statement "a composed function is a function" in the context of mathematics, in NEML2, the equivalent statement "a `ComposedModel` is a `Model`" also holds. In addition, the `ComposedModel` provides four key features to help simplify the composition and reduces computational cost: +- Automatic dependency registration +- Automatic input/output identification +- Automatic dependency resolution +- Automatic chain rule + +### A symbolic example {#a-symbolic-example} + +To demonstrate the utility of the four key features of `ComposedModel`, let us consider the composition of three functions \f$f\f$, \f$g\f$, and \f$h\f$: +\f{align*} + y_1 &= f(x_1, x_2), \\ + y_2 &= g(y_1, x_3), \\ + y &= h(y_1, y_2, x_4). +\f} + +### Automatic dependency registration {#automatic-dependency-registration} + +It is obvious to us that the function \f$h\f$ _depends_ on functions \f$f\f$ and \f$g\f$ because the input of \f$h\f$ depends on the outputs of \f$f\f$ and \f$g\f$. Such dependency is automatically identified and registered while composing a `ComposedModel` in NEML2. This procedure is called "automatic dependency registration". + +In order to identify dependencies among different `Model`s, we keep track of the set of _consumed_ variables, \f$\mathcal{I}_i\f$, and a set of _provided_ variables, \f$\mathcal{O}_i\f$, for each `Model` \f$f_i\f$. When a set of models (functions) are composed together, `Model` \f$f_i\f$ is said to _depend_ on \f$f_j\f$ if and only if \f$\exists x\f$ such that +\f[ + x \in \mathcal{I}_i \wedge x \in \mathcal{O}_j. +\f] + +### Automatic input/output identification {#automatic-input-output-identification} + +The only possible composition \f$r\f$ of these three functions is +\f[ + y = r(x_1, x_2, x_3, x_4) := h(f(x_1, x_2), g(f(x_1, x_2), x_3), x_4). +\f] +The input variables of the composed function \f$r\f$ are \f$[x_1, x_2, x_3, x_4]\f$ (or their flattened concatenation), and the output variable of the composed function is simply \f$y\f$. The input/output variables are automatically identified while composing a `ComposedModel` in NEML2. This procedure is referred to as "automatic input/output identification". + +In a `ComposedModel`, a _leaf_ model is a model which does not depend on any other model, and a _root_ model is a model which is not depent upon by any other model. A `ComposedModel` may have multiple leaf models and multiple root models. An input variable is said to be a _root_ input variable if it is not consumed by any other model, i.e. \f$x \in \mathcal{I}_i\f$ is a root input variable if and only if +\f[ + x \notin \mathcal{O}_j, \quad \forall i \neq j. +\f] +Similarly, an output variable is said to be a _leaf_ output variable if it is not provided by any other model, i.e. \f$x \in \mathcal{O}_i\f$ is a leaf output variable if an only if +\f[ + x \notin \mathcal{I}_j, \quad \forall i \neq j. +\f] +The input variables of a `ComposedModel` is the union of the set of all the root input variables, and the output variables of a `ComposedModel` is the set of all the leaf output variables. + +### Automatic dependency resolution {#automatic-dependency-resolution} + +To evaluate the forward operator of the composed model \f$r\f$, one has to first evaluate model \f$f\f$, then model \f$g\f$, and finally model \f$h\f$. The process of sorting out such evaluation order is called "dependency resolution". + +While it is possible to sort the evaluation order "by hand" for this simple example composition, it is generally not a trivial task for practical compositions with more involved dependencies. To that end, NEML2 uses [topological sort](https://en.wikipedia.org/wiki/Topological_sorting) to sort the model evaluation order, such that by the time each model is evaluated, all of its dependent models have already been evaluated. + +### Automatic chain rule {#automatic-chain-rule} + +Chain rule can be applied to evaluate the derivative of the forward operator with respect to the input variables, i.e., +\f{align*} + \frac{\partial y}{\partial x_1} &= \left( \frac{\partial y}{\partial y_1} + \frac{\partial y}{\partial y_2} \frac{\partial y_2}{\partial y_1} \right) \frac{\partial y_1}{\partial x_1}, \\ + \frac{\partial y}{\partial x_2} &= \left( \frac{\partial y}{\partial y_1} + \frac{\partial y}{\partial y_2} \frac{\partial y_2}{\partial y_1} \right) \frac{\partial y_1}{\partial x_2}, \\ + \frac{\partial y}{\partial x_3} &= \frac{\partial y}{\partial y_2} \frac{\partial y_2}{\partial x_3}, \\ + \frac{\partial y}{\partial x_4} &= \frac{\partial y}{\partial x_4}. +\f} +Spelling out this chain rule can be cumbersome and error-prone, especially for more complicated model compositions. The evaluation of the chain rule is automated in NEML2, and the user is only responsible for implementing the partial derivatives of each model. For example, in the implementation of `Model` \f$f\f$, the user only need to define the partial derivatives +\f[ + \frac{\partial y_1}{\partial x_1}, \quad \frac{\partial y_1}{\partial x_2}; +\f] +similarly, `Model` \f$g\f$ only defines +\f[ + \frac{\partial y_2}{\partial y_1}, \quad \frac{\partial y_2}{\partial x_3} +\f] +and `Model` \f$h\f$ only defines +\f[ + \frac{\partial y}{\partial y_1}, \quad \frac{\partial y}{\partial y_2}, \quad \frac{\partial y}{\partial x_4}. +\f] +The assembly of the partial derivatives into the total derivative \f$\partial y / \partial \mathbf{x}\f$ using the chain rule is handled by NEML2. This design serves as the fundation for a modular model implementation: +- Each model _does not_ need to know its composition with others. +- The same model partial derivatives can be reused in _any_ composition. diff --git a/doc/content/_02_04_solver.md b/doc/content/_02_04_solver.md new file mode 100644 index 0000000000..c4b949c682 --- /dev/null +++ b/doc/content/_02_04_solver.md @@ -0,0 +1,3 @@ +# Solver {#solver} + +[TOC] diff --git a/doc/content/_02_05_driver.md b/doc/content/_02_05_driver.md new file mode 100644 index 0000000000..18fbfb3582 --- /dev/null +++ b/doc/content/_02_05_driver.md @@ -0,0 +1,3 @@ +# Driver {#driver} + +[TOC] diff --git a/doc/content/_02_system.md b/doc/content/_02_system.md new file mode 100644 index 0000000000..8b914ca99e --- /dev/null +++ b/doc/content/_02_system.md @@ -0,0 +1,11 @@ +# System Documentation {#system} + +| System | Input file entry point | Description | +| :-------------------- | :--------------------- | :------------------------------------------ | +| [Tensor](@ref tensor) | `[Tensors]` | Tensors created at run-time | +| [Data](@ref data) | `[Data]` | Data used by models | +| [Model](@ref model) | `[Models]` | The model or the composition of models | +| [Solver](@ref solver) | `[Solvers]` | Solvers used to solve a system of equations | +| [Driver](@ref driver) | `[Drivers]` | Predefined drivers to run models | + +Follow the above links for for in-depth explanation of each system. diff --git a/doc/content/_02_user.md b/doc/content/_02_user.md deleted file mode 100644 index 6aae98069b..0000000000 --- a/doc/content/_02_user.md +++ /dev/null @@ -1,3 +0,0 @@ -# User Guide {#user} - -[TOC] diff --git a/doc/content/_03_dev.md b/doc/content/_03_dev.md deleted file mode 100644 index 23def9ffae..0000000000 --- a/doc/content/_03_dev.md +++ /dev/null @@ -1,533 +0,0 @@ -# Developer Guide {#dev} - -[TOC] - -## Tensor types {#tensor-types} - -Currently, PyTorch is the only supported tensor backend in NEML2. Therefore, all tensor types in NEML2 directly inherit from `torch::Tensor`. In the future, support for other tensor backends may be added, but the public-facing interfaces will remain largely the same. - -### Dynamically shaped tensor {#dynamically-shaped-tensor} - -[BatchTensor](@ref neml2::BatchTensor) is a general-purpose *dynamically shaped* tensor type for batched tensors. With a view towards vectorization, the same set of operations can be "simultaneously" applied to a "batch" of (logically the same) tensors. To provide a unified user interface for dealing with such batched operation, NEML2 assumes that the *first* \f$N\f$ dimensions of a tensor are batched dimensions, and the following dimensions are the base (logical) dimensions. - -> Unlike PyTorch, NEML2 explicitly distinguishes between batch dimensions and base (logical) dimensions. - -A `BatchTensor` can be created using -```cpp -BatchTensor A(torch::rand({1, 1, 5, 2}), 2); -``` -where `A` is a tensor with 2 batch dimensions. The batch sizes of `A` is `(1, 1)`: -```cpp -auto batch_sz = A.batch_sizes(); -neml2_assert(batch_sz == {1, 1}); -``` -and the base (logical) sizes of `A` is `(5, 2)`: -```cpp -auto base_sz = A.base_sizes(); -neml2_assert(batch_sz == {5, 2}); -``` - -### Statically shaped tensors {#statically-shaped-tensor} - -[FixedDimTensor](@ref neml2::FixedDimTensor) is the parent class for all the tensor types with a *fixed* base shape. It is templated on the base shape of the tensor. NEML2 offers a rich collection of primitive tensor types inherited from `FixedDimTensor`. Currently implemented primitive tensor types are summarized below - -| Tensor type | Base shape | Description | -| :------------------------------------- | :---------------- | :--------------------------------------------------------------- | -| [Scalar](@ref neml2::Scalar) | \f$()\f$ | Rank-0 tensor, i.e. scalar | -| [Vec](@ref neml2::Vec) | \f$(3)\f$ | Rank-1 tensor, i.e. vector | -| [R2](@ref neml2::R2) | \f$(3,3)\f$ | Rank-2 tensor | -| [SR2](@ref neml2::SR2) | \f$(6)\f$ | Symmetric rank-2 tensor | -| [WR2](@ref neml2::WR2) | \f$(3)\f$ | Skew-symmetric rank-2 tensor | -| [R3](@ref neml2::R3) | \f$(3,3,3)\f$ | Rank-3 tensor | -| [SFR3](@ref neml2::SFR3) | \f$(6,3)\f$ | Rank-3 tensor with symmetry on base dimensions 0 and 1 | -| [R4](@ref neml2::R4) | \f$(3,3,3,3)\f$ | Rank-4 tensor | -| [SSR4](@ref neml2::SSR4) | \f$(6,6)\f$ | Rank-4 tensor with minor symmetry | -| [R5](@ref neml2::R5) | \f$(3,3,3,3,3)\f$ | Rank-5 tensor | -| [SSFR5](@ref neml2::SSFR5) | \f$(6,6,3)\f$ | Rank-5 tensor with minor symmetry on base dimensions 0-3 | -| [Rot](@ref neml2::Rot) | \f$(3)\f$ | Rotation tensor represented in the Rodrigues form | -| [Quarternion](@ref neml2::Quarternion) | \f$(4)\f$ | Quarternion | -| [MillerIndex](@ref neml2::MillerIndex) | \f$(3)\f$ | Crystal direction or lattice plane represented as Miller indices | - -Furthermore, all primitive tensor types can be "registered" as variables on a `LabeledAxis`, which will be discussed in the following section on [labeled view](@ref tensor-labeling). - -## Working with tensors {#working-with-tensors} - -### Tensor creation {#tensor-creation} - -A factory tensor creation function produces a new tensor. All factory functions adhere to the same schema: -```cpp -::(, const torch::TensorOptions & options); -``` -where `` is the class name of the primitive tensor type listed above, and `` is the name of the factory function which produces the new tensor. `` are any required or optional arguments a particular factory function accepts. Refer to each tensor type's class documentation for the concrete signature. The last argument `const torch::TensorOptions & options` configures the data type, device, layout and other "meta" properties of the produced tensor. The commonly used meta properties are -- `dtype`: the data type of the elements stored in the tensor. Available options are `kUInt8`, `kInt8`, `kInt16`, `kInt32`, `kInt64`, `kFloat32`, and `kFloat64`. -- `layout`: the striding of the tensor. Available options are `kStrided` (dense) and `kSparse`. -- `device`: the compute device where the tensor will be allocated. Available options are `kCPU` and `kCUDA`. -- `requires_grad`: whether the tensor is part of a function graph used by automatic differentiation to track functional relationship. Available options are `true` and `false`. - -For example, the following code -```cpp -auto a = SR2::zeros({5, 3}, - torch::TensorOptions() - .device(torch::kCPU) - .layout(torch::kStrided) - .dtype(torch::kFloat32)); -``` -creates a statically (base) shaped, dense, single precision tensor of type `SR2` filled with zeros, with batch shape \f$(5, 3)\f$, allocated on the CPU. - -### Tensor broadcasting {#tensor-broadcasting} - -Quoting Numpy's definition of broadcasting: - -> The term broadcasting describes how NumPy treats arrays with different shapes during arithmetic operations. Subject to certain constraints, the smaller array is “broadcast” across the larger array so that they have compatible shapes. - -NEML2's broadcasting semantics is largely the same as those of Numpy and PyTorch. However, since NEML2 explicitly distinguishes between batch and base dimensions, the broadcasting semantics must also be extended. Two NEML2 tensors are said to be _batch-broadcastable_ if iterating backward from the last batch dimension, one of the following is satisfied: -1. Both tensors have the same size on the dimension; -2. One tensor has size 1 on the dimension; -3. The dimension does not exist in one tensor. - -_Base-broadcastable_ follows a similar definition. Most binary operators on dynamically shaped tensors, i.e., those of type `BatchTensor`, require the operands to be both batch- _and_ base-broadcastable. On the other hand, most binary operators on statically base shaped tensors, i.e., those of pritimitive tensor types, only require the operands to be batch-broadcastable. - -### Tensor indexing {#tensor-indexing} - -In defining the forward operator of a material model, many logically different tensors representing inputs, outputs, residuals, and Jacobians have to be created, copied, and destroyed on the fly. These operations occupy a significant amount of computing time, especially on GPUs. - -To address this challenge, NEML2 creates *views*, instead of copies, of tensors whenever possible. As its name suggests, the view of a tensor is a possibly different interpretation of the underlying data. Quoting the PyTorch documentation: - -> For a tensor to be viewed, the new view size must be compatible with its original size and stride, i.e., each new view dimension must either be a subspace of an original dimension, or only span across original dimensions \f$d, d+1, ..., d+k\f$ that satisfy the following contiguity-like condition that \f$\forall i = d,...,d+k-1\f$, -> \f[ -> \text{stride}[i] = \text{stride}[i+1] \times \text{size}[i+1] -> \f] -> Otherwise, it will not be possible to view self tensor as shape without copying it. - -In NEML2, use [base_index](@ref neml2::BatchTensorBase::base_index) for indexing the base dimensions and [batch_index](@ref neml2::BatchTensorBase::batch_index) for indexing the batch dimensions: -```cpp -using namespace torch::indexing; -BatchTensor A(torch::tensor({{2, 3, 4}, {-1, -2, 3}, {6, 9, 7}}), 1); -// A = [[ 2 3 4] -// [ -1 -2 3] -// [ 6 9 7]] -BatchTensor B = A.batch_index({Slice(0, 2)}); -// B = [[ 2 3 4] -// [ -1 -2 3]] -BatchTensor C = A.base_index({Slice(1, 3)}); -// C = [[ 3 4] -// [ -2 3] -// [ 9 7]] -``` -To modify the content of a tensor, use [base_index_put](@ref neml2::BatchTensorBase::base_index_put) or [batch_index_put](@ref neml2::BatchTensorBase::batch_index_put): -```cpp -A.base_index_put({Slice(1, 3)}, torch::ones({3, 2})); -// A = [[ 2 1 1] -// [ -1 1 1] -// [ 6 1 1]] -A.batch_index_put({Slice(0, 2)}, torch::zeros({2, 3})); -// A = [[ 0 0 0] -// [ 0 0 0] -// [ 6 1 1]] -``` -A detailed explanation on tensor indexing APIs is available as part of the official [PyTorch documentation](https://pytorch.org/cppdocs/notes/tensor_indexing.html). - -### Tensor labeling {#tensor-labeling} - -In the context of material modeling, oftentimes views of tensors have practical/physical meanings. For example, given a logically 1D tensor with base size 9, its underlying data in an arbitrary batch may look like -``` -equivalent plastic strain 2.1 - cauchy stress -2.1 - 0 - 1.3 - -1.1 - 2.5 - 2.5 - temperature 102.9 - time 3.6 -``` -where component 0 stores the scalar-valued equivalent plastic strain, components 1-6 store the tensor-valued cauchy stress (we use the Mandel notation for symmetric second order tensors), component 7 stores the scalar-valued temperature, and component 8 stores the scalar-valued time. - -The string indicating the physical meaning of the view, e.g., "cauchy stress", is called a "label", and the view of the tensor indexed by a label is called a "labeled view", i.e., -``` - cauchy stress -2.1 - 0 - 1.3 - -1.1 - 2.5 - 2.5 -``` - -NEML2 provides a data structure named [LabeledAxis](@ref neml2::LabeledAxis) to facilitate the creation and modification of labels, and a data structure named [LabeledTensor](@ref neml2::LabeledTensor) to facilitate the creation and modification of labeled views. - -The [LabeledAxis](@ref neml2::LabeledAxis) contains all information regarding how an axis of a `LabeledTensor` is labeled. The following naming convention is used: -- Item: A labelable slice of data -- Variable: An item that is also of a [NEML2 primitive tensor type](@ref tensor-types) -- Sub-axis: An item of type `LabeledAxis` - -So yes, an axis can be labeled recursively, e.g., - -``` - 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 -/// |-----------| |-----| | | | | | | -/// a b | | | | | | -/// |-------------------| |--------------| |--| |--| -/// sub a b c -``` -The above example represents an axis of size 15. This axis has 4 items: `a`, `b`, `c`, and `sub`. -- "a" is a variable of storage size 6 (possibly of type `SR2`). -- "b" is a variable of type `Scalar`. -- "c" is a variable of type `Scalar`. -- "sub" is a sub-axis of type `LabeledAxis`. "sub" by itself represents an axis of size 7, containing 2 items: - - "a" is a variable of storage size 6. - - "b" is a variable of type `Scalar`. - -Duplicate labels are *not* allowed on the same level of the axis, e.g. "a", "b", "c", and "sub" share the same level and so must be different. However, items on different levels of an axis can share the same label, e.g., "a" on the sub-axis "sub" has the same label as "a" on the main axis. In NEML2 convention, item names are always fully qualified, and a sub-axis is prefixed with a left slash, e.g. item "b" on the sub-axis "sub" can be denoted as "sub/b" on the main axis. - -> A label cannot contain: white spaces, quotes, left slash (`/`), or new line. -> -> Due to performance considerations, a `LabeledAxis` can only be modified, e.g., adding/removing variables and sub-axis, at the time a model is constructed. After the model construction phase, the `LabeledAxis` associated with that model can no longer be modified over the entire course of the simulation. - -Refer to the documentation for a complete list of APIs for creating and modifying a [LabeledAxis](@ref neml2::LabeledAxis). - -[LabeledTensor](@ref neml2::LabeledTensor) is the primary data structure in NEML2 for working with labeled tensor views. Each `LabeledTensor` consists of one `BatchTensor` and one or more `LabeledAxis`s. The `LabeledTensor` is templated on the base dimension \f$D\f$. [LabeledVector](@ref neml2::LabeledVector) and [LabeledMatrix](@ref neml2::LabeledMatrix) are the two most widely used data structures in NEML2. - -`LabeledTensor` handles the creation, modification, and accessing of labeled tensors. Recall that all primitive data types in a labeled tensor are flattened, e.g., a symmetric fourth order tensor of type `SSR4` with batch size `(5)` and base size `(6, 6)` are flattened to have base size `(36)` in the labeled tensor. The documentation provides a complete list of APIs. The commonly used methods are -- [operator()](@ref neml2::LabeledTensor::operator()()) for retrieving a labeled view into the raw (flattened) data without reshaping -- [get](@ref neml2::LabeledTensor::get) for retrieving a labeled view and reshaping it to the correct shape -- [set](@ref neml2::LabeledTensor::set) for setting values for a labeled view -- [slice](@ref neml2::LabeledTensor::slice) for slicing a sub-axis along a specific base dimension -- [block](@ref neml2::LabeledTensor::block) for sub-indexing the `LabeledTensor` with \f$D\f$ sub-axis names - -## Model {#model} - -### Model definition {#model-definition} - -A NEML2 model is a function (in the context of mathematics) -\f[ - f: \mathbb{R}^m \to \mathbb{R}^n -\f] -mapping from the input space \f$\mathbb{R}^m\f$ of dimension \f$m\f$ to the output space \f$\mathbb{R}^n\f$ of dimension \f$n\f$. \f$\left[ \cdot \right]\f$ be the flatten-concatenation operator, the input vector is the concatenation of \f$p\f$ flattened variables, i.e., -\f[ - x = \left[ x_i \right]_{i=1}^p \in \mathbb{R}^m, \quad \sum_{i=1}^p \lvert x_i \rvert = m, -\f] -where \f$\lvert x \rvert\f$ denotes the modulus of flattened variable \f$x\f$. Similarly, the output vector is the concatenation of \f$q\f$ flattened variables, i.e., -\f[ - y = \left[ y_i \right]_{i=1}^q \in \mathbb{R}^n, \quad \sum_{i=1}^q \lvert y_i \rvert = n. -\f] - -Translating the above mathematical definition into NEML2 is straightforward. -- A model following this definition derives from [Model](@ref neml2::Model). -- [declare_input_variable](@ref neml2::Model::declare_input_variable) declares an input variable \f$x_i\f$ in the input space \f$\mathbb{R}^m\f$. -- [declare_output_variable](@ref neml2::Model::declare_output_variable) declares an output variable \f$y_i\f$ in the output space \f$\mathbb{R}^n\f$. -- [set_value](@ref neml2::Model::set_value) is a method defining the forward operator \f$f\f$ itself. - -Both `declare_input_variable` and `declare_output_variable` are templated on the variable type -- recall that only a variable of the NEML2 primitive tensor type can be registered. Furthermore, both methods return a `Variable &` used for retrieving and setting the variable value inside the forward operator, i.e. `set_value`. Note that the reference returned by `declare_input_variable` is writable, while the reference returned by `declare_output_variable` is read-only. - -### Model composition {#model-composition} - -Quoting [Wikipedia](https://en.wikipedia.org/wiki/Function_composition): -> In mathematics, function composition is an operation \f$\circ\f$ that takes two functions \f$f\f$ and \f$g\f$, and produces a function \f$h = g \circ f\f$ such that \f$h(x) = g(f(x))\f$. - -Since NEML2 `Model` is a function (in the context of mathematics) at its core, it should be possible, in theory, to compose different NEML2 `Model`s into a new NEML2 `Model`. The [ComposedModel](@ref neml2::ComposedModel) is precisely for that purpose. - -Similar to the statement "a composed function is a function" in the context of mathematics, in NEML2, the equivalent statement "a `ComposedModel` is a `Model`" also holds. In addition, the `ComposedModel` provides four key features to help simplify the composition and reduces computational cost: -- Automatic dependency registration -- Automatic input/output identification -- Automatic dependency resolution -- Automatic chain rule - -### A symbolic example {#a-symbolic-example} - -To demonstrate the utility of the four key features of `ComposedModel`, let us consider the composition of three functions \f$f\f$, \f$g\f$, and \f$h\f$: -\f{align*} - y_1 &= f(x_1, x_2), \\ - y_2 &= g(y_1, x_3), \\ - y &= h(y_1, y_2, x_4). -\f} - -### Automatic dependency registration {#automatic-dependency-registration} - -It is obvious to us that the function \f$h\f$ _depends_ on functions \f$f\f$ and \f$g\f$ because the input of \f$h\f$ depends on the outputs of \f$f\f$ and \f$g\f$. Such dependency is automatically identified and registered while composing a `ComposedModel` in NEML2. This procedure is called "automatic dependency registration". - -In order to identify dependencies among different `Model`s, we keep track of the set of _consumed_ variables, \f$\mathcal{I}_i\f$, and a set of _provided_ variables, \f$\mathcal{O}_i\f$, for each `Model` \f$f_i\f$. When a set of models (functions) are composed together, `Model` \f$f_i\f$ is said to _depend_ on \f$f_j\f$ if and only if \f$\exists x\f$ such that -\f[ - x \in \mathcal{I}_i \wedge x \in \mathcal{O}_j. -\f] - -### Automatic input/output identification {#automatic-input-output-identification} - -The only possible composition \f$r\f$ of these three functions is -\f[ - y = r(x_1, x_2, x_3, x_4) := h(f(x_1, x_2), g(f(x_1, x_2), x_3), x_4). -\f] -The input variables of the composed function \f$r\f$ are \f$[x_1, x_2, x_3, x_4]\f$ (or their flattened concatenation), and the output variable of the composed function is simply \f$y\f$. The input/output variables are automatically identified while composing a `ComposedModel` in NEML2. This procedure is referred to as "automatic input/output identification". - -In a `ComposedModel`, a _leaf_ model is a model which does not depend on any other model, and a _root_ model is a model which is not depent upon by any other model. A `ComposedModel` may have multiple leaf models and multiple root models. An input variable is said to be a _root_ input variable if it is not consumed by any other model, i.e. \f$x \in \mathcal{I}_i\f$ is a root input variable if and only if -\f[ - x \notin \mathcal{O}_j, \quad \forall i \neq j. -\f] -Similarly, an output variable is said to be a _leaf_ output variable if it is not provided by any other model, i.e. \f$x \in \mathcal{O}_i\f$ is a leaf output variable if an only if -\f[ - x \notin \mathcal{I}_j, \quad \forall i \neq j. -\f] -The input variables of a `ComposedModel` is the union of the set of all the root input variables, and the output variables of a `ComposedModel` is the set of all the leaf output variables. - -### Automatic dependency resolution {#automatic-dependency-resolution} - -To evaluate the forward operator of the composed model \f$r\f$, one has to first evaluate model \f$f\f$, then model \f$g\f$, and finally model \f$h\f$. The process of sorting out such evaluation order is called "dependency resolution". - -While it is possible to sort the evaluation order "by hand" for this simple example composition, it is generally not a trivial task for practical compositions with more involved dependencies. To that end, NEML2 uses [topological sort](https://en.wikipedia.org/wiki/Topological_sorting) to sort the model evaluation order, such that by the time each model is evaluated, all of its dependent models have already been evaluated. - -### Automatic chain rule {#automatic-chain-rule} - -Chain rule can be applied to evaluate the derivative of the forward operator with respect to the input variables, i.e., -\f{align*} - \frac{\partial y}{\partial x_1} &= \left( \frac{\partial y}{\partial y_1} + \frac{\partial y}{\partial y_2} \frac{\partial y_2}{\partial y_1} \right) \frac{\partial y_1}{\partial x_1}, \\ - \frac{\partial y}{\partial x_2} &= \left( \frac{\partial y}{\partial y_1} + \frac{\partial y}{\partial y_2} \frac{\partial y_2}{\partial y_1} \right) \frac{\partial y_1}{\partial x_2}, \\ - \frac{\partial y}{\partial x_3} &= \frac{\partial y}{\partial y_2} \frac{\partial y_2}{\partial x_3}, \\ - \frac{\partial y}{\partial x_4} &= \frac{\partial y}{\partial x_4}. -\f} -Spelling out this chain rule can be cumbersome and error-prone, especially for more complicated model compositions. The evaluation of the chain rule is automated in NEML2, and the user is only responsible for implementing the partial derivatives of each model. For example, in the implementation of `Model` \f$f\f$, the user only need to define the partial derivatives -\f[ - \frac{\partial y_1}{\partial x_1}, \quad \frac{\partial y_1}{\partial x_2}; -\f] -similarly, `Model` \f$g\f$ only defines -\f[ - \frac{\partial y_2}{\partial y_1}, \quad \frac{\partial y_2}{\partial x_3} -\f] -and `Model` \f$h\f$ only defines -\f[ - \frac{\partial y}{\partial y_1}, \quad \frac{\partial y}{\partial y_2}, \quad \frac{\partial y}{\partial x_4}. -\f] -The assembly of the partial derivatives into the total derivative \f$\partial y / \partial \mathbf{x}\f$ using the chain rule is handled by NEML2. This design serves as the fundation for a modular model implementation: -- Each model _does not_ need to know its composition with others. -- The same model partial derivatives can be reused in _any_ composition. - -## Developing custom model {#custom-model} - -The following tutorials serve as a developer-facing step-by-step guide for creating and testing a custom material model. A simple linear isotropic hardening is used as the example in this tutorial. The model can be mathematically written as -\f{align*} - k &= H \bar{\varepsilon}^p, -\f} -where \f$\bar{\varepsilon}^p\f$ is the equivalent plastic strain and \f$k\f$ is the isotropic hardening. The input variable for this model is -\f$\mathbf{\varepsilon}\f$, the output variable for this model is \f$k\f$, and the parameters of the model is \f$H\f$. - -### Naming conventions {#naming-conventions} - -Recall that NEML2 models operates on _labeled tensors_, and that the collection of labels (with their corresponding layout) is called an labeled axis ([LabeledAxis](@ref neml2::LabeledAxis)). NEML2 predefines 5 sub-axes to categorize all the input, output and intermediate variables: -- State \f$\mathcal{S}\f$: Variables on the "state" sub-axis collectively characterize the current _state_ of the material subject to given external forces. The state variables are usually the output of a physically meaningful material model. -- Forces \f$\mathcal{F}\f$: Variables on the "forces" sub-axis define the _external_ forces that drive the response of the material. -- Old state \f$\mathcal{S}_n\f$: The state variables _prior to_ the current material update. In the time-discrete setting, these are the state variables from the previous time step. -- Old forces \f$\mathcal{F}_n\f$: The external forces _prior to_ the current material update. In the time-discrete setting, these are the forces from the previous time step. -- Residual \f$\mathcal{R}\f$: The residual defines an _implicit_ model/function. An implicit model is updated by solving for the state variables that result in zero residual. - -In NEML2, the following naming conventions are recommended: -- User-facing variables and option names should be _as descriptive as possible_. For example, the equivalent plastic strain is named "equivalent_plastic_strain". Note that white spaces, quotes, and left slashes are not allowed in the names. Underscores are recommended as an replacement for white spaces. -- Developer-facing variables and option names should use simple alphanumeric symbols. For example, the equivalent plastic strain is named "ep" in consistency with most of the existing literature. -- Developner-facing member variables and option names should use the same alphanumeric symbols. For example, the member variable for the equivalent plastic strain is named `ep`. However, if the member variable is protected or private, it is recommended to prefix it with an underscore, i.e. `_ep`. -- Struct names and class names should use `PascalCase`. -- Function names should use `snake_case`. - -### Declaring variables {#declaring-variables} - -The development of every model begins with the declaration and registration of its input and output variables. Here, we first define an abstract base class that will be later used to define the linear isotropic hardening relation. The abstract base class defines the isotropic hardening relation -\f[ - k = f\left( \bar{\varepsilon}^p \right), -\f] -mapping the equivalent plastic strain to the isotropic hardening. The base class is named `IsotropicHardening` following the [naming conventions](@ref naming-conventions). The header file [IsotropicHardening.h](@ref neml2::IsotropicHardening) is displayed below -```cpp -#pragma once - -#include "neml2/models/Model.h" - -namespace neml2 -{ -class IsotropicHardening : public Model -{ -public: - static OptionSet expected_options(); - - IsotropicHardening(const OptionSet & options); - -protected: - /// Equivalent plastic strain - const Variable & _ep; - - /// Isotropic hardening - Variable & _h; -}; -} // namespace neml2 -``` -Since isotropic hardening _is a_ model, the class inherits from `Model`. The user-facing expected options are defined by the static method `expected_options`. NEML2 handles the parsing of user-specified options and pass them to the constructor. The input variable of the model is the equivalent plastic strain, and the output variable of the model is the isotropic hardening. Their corresponding variable value references are stored as `_ep` and `_h`, respectively, again following the [naming conventions](@ref naming-conventions). - -The expected options and the constructor are defined as -```cpp -#include "neml2/models/solid_mechanics/IsotropicHardening.h" - -namespace neml2 -{ -OptionSet -IsotropicHardening::expected_options() -{ - OptionSet options = Model::expected_options(); - options.set("equivalent_plastic_strain") = VariableName("state", "internal", "ep"); - options.set("isotropic_hardening") = VariableName("state", "internal", "k"); - return options; -} - -IsotropicHardening::IsotropicHardening(const OptionSet & options) - : Model(options), - _ep(declare_input_variable("equivalent_plastic_strain")), - _h(declare_output_variable("isotropic_hardening")) -{ -} -} // namespace neml2 -``` -Recall that variable names on `LabeledAxis` are always fully qualified, the equivalent plastic strain and the isotropic hardening are denoted as "state/internal/ep" and "state/internal/k", respectively. An instance of the class is constructed by extracting user-specified options (of type `OptionSet`). Note how `declare_input_variable` and `declare_output_variable` are used to declare and register the input and output variables. - -### Declaring parameters {#declaring-parameters} - -Now that the abstract base class `IsotropicHardening` has been implemented, we are ready to define our first concrete NEML2 model that describes a linear isotropic hardening relation -\f[ - k = H \bar{\varepsilon}^p. -\f] -Note that \f$H\f$ is a model parameter. Following the naming convention, the concrete class is named `LinearIsotropicHardening`. The header file is displayed below. -```cpp -#pragma once - -#include "neml2/models/solid_mechanics/IsotropicHardening.h" - -namespace neml2 -{ -/** - * @brief Simple linear map between equivalent strain and hardening - * - */ -class LinearIsotropicHardening : public IsotropicHardening -{ -public: - static OptionSet expected_options(); - - LinearIsotropicHardening(const OptionSet & options); - -protected: - void set_value(bool out, bool dout_din, bool d2out_din2) override; - - const Scalar & _K; -}; -} // namespace neml2 -``` -It derives from the abstract base class `IsotropicHardening` and implements the method `set_value` as the forward operator. The model parameter \f$H\f$ is stored as a protected member variable `_K`. The model implementation is shown below. -```cpp -#include "neml2/models/solid_mechanics/LinearIsotropicHardening.h" - -namespace neml2 -{ -register_NEML2_object(LinearIsotropicHardening); - -OptionSet -LinearIsotropicHardening::expected_options() -{ - OptionSet options = IsotropicHardening::expected_options(); - options.set>("hardening_modulus"); - return options; -} - -LinearIsotropicHardening::LinearIsotropicHardening(const OptionSet & options) - : IsotropicHardening(options), - _K(declare_parameter("K", "hardening_modulus")) -{ -} - -void -LinearIsotropicHardening::set_value(bool out, bool dout_din, bool d2out_din2) -{ - if (out) - _h = _K * _ep; - - if (dout_din) - _h.d(_ep) = _K; - - if (d2out_din2) - { - // zero - } -} -} // namespace neml2 -``` -Note that an additional option named "hardening_modulus" is requested from the user. The model parameter is registered using the API `declare_parameter`. In the `set_value` method, the current value of the input variable, equivalent plastic strain, is stored in the member `_ep`, and so the isotropic hardening can be computed as -```cpp -_K * _ep -``` -The computed result is copied into the model output variable `_h` by -```cpp -_h = _K * _ep; -``` -In addition, the first derivative of the forward operator is defined as -```cpp -_h.d(_ep) = _K; -``` -Last but not the least, the model is registed in the NEML2 model factory using the macro -```cpp -register_NEML2_object(LinearIsotropicHardening); -``` -so that an instance of the class can be created at runtime. - -### Testing {#testing} - -It is of paramount importance to ensure the correctness of the implementation. NEML2 offers 5 types of tests with different purposes. - -A Catch2 test refers to a test directly written in C++ source code within the Catch2 framework. It offers the highest level of flexibility, but requires more effort to set up. To understand how a Catch2 test works, please refer to the [official Catch2 documentation](https://github.com/catchorg/Catch2/blob/v2.x/docs/tutorial.md). - -A model unit test examines the outputs of a `Model` given a predefined set of inputs. Model unit tests can be directly designed using the input file syntax with the `ModelUnitTest` type. A variety of checks can be turned on and off based on input file options. To list a few: `check_first_derivatives` compares the implemented first order derivatives of the model against finite-differencing results, and the test is marked as passing only if the two derivatives are within tolerances specified with `derivative_abs_tol` and `derivative_rel_tol`; if `check_cuda` is set to `true`, all checks are repeated twice, once on CPU and once on GPU (if available), and pass only if the two evaluations yield same results within tolerances. - -All input files for model unit tests should be stored inside `tests/unit/models`. Every input file with the `.i` extension will be automatically discovered and executed. To run all the model unit tests, use the following commands -``` -cd tests -./unit_tests models -``` - -To run a specific model unit test, use the `-c` command line option followed by the relative location of the input file, i.e. -``` -cd tests -./unit_tests models -c solid_mechanics/LinearIsotropicElasticity.i -``` - -A model regression test runs a `Model` using a user specified driver. The results are compared against a predefined reference (stored on the disk checked into the repository). The test passes only if the current results are the same as the predefined reference (again within specified tolerances). The regression tests ensure the consistency of implementations across commits. Currently, `TransientRegression` is the only supported type of regression test. - -Each input file for model regression tests should be stored inside a separate folder inside `tests/regression`. Every input file with the `.i` extension will be automatically discovered and executed. To run all the model regression tests, use the `regression_tests` executable followed by the physics module, i.e. -``` -cd tests -./regression_tests "solid mechanics" -``` -To run a specific model regression test, use the `-c` command line option followed by the relative location of the input file, i.e. -``` -cd tests -./regression_tests "solid mechanics" -c viscoplasticity/chaboche/model.i -``` -Note that the regression test expects an option `reference` which specifies the relative location to the reference solution. - -The model verification test is similar to the model regression test in terms of workflow. The difference is the a verification test defines the reference solution using NEML, the predecessor of NEML2. Since NEML was developed with strict software assurance, the verification tests ensure that the migration from NEML to NEML2 does not cause any regression in software quality. - -Each input file for model verification tests should be stored inside a separate folder inside `tests/verification`. Every input file with the `.i` extension will be automatically discovered and executed. To run all the model verification tests, use the `verification_tests` executable followed by the physics module, i.e. -``` -cd tests -./verification_tests "solid mechanics" -``` - -To run a specific model verification test, use the `-c` command line option followed by the relative location of the input file, i.e. -``` -cd tests -./verification_tests "solid mechanics" -c chaboche/chaboche.i -``` -The regression test compares variables (specified using the `variables` option) against reference values (specified using the `references` option). The reference variables can be read using input objects with type `VTestTimeSeries`. - -### Benchmarking {#benchmarking} - -The benchmark tests can be authored within the [Catch2 microbenchmarking framework](https://github.com/catchorg/Catch2/blob/v2.x/docs/benchmarks.md). Before any benchmarks can be executed, the clock's resolution is estimated. A few other environmental artifacts are also estimated at this point, like the cost of calling the clock function, but they almost never have any impact in the results. The user code is executed a few times to obtain an estimate of the amount of runs that should be in each sample. This also has the potential effect of bringing relevant code and data into the caches before the actual measurement starts. Finally, all the samples are collected sequentially by performing the number of runs estimated in the previous step for each sample. - -To run a benchmark test, use the `benchmark.sh` script inside the `scripts` directory with 3 positional arguments: -``` -./scripts/benchmark.sh Chaboche 5 timings -``` -The first positional argument specifies the name of the benchmark test to run. The second positional argument specifies the number of samples to repeat in each iteration. The third positional argument specifies the output directory of the benchmark results. - -The Chaboche benchmark test is repeated with different batch sizes and on different devices (in this case CPU and GPU). The final benchmark results are summarized in the following figure. - -![Chaboche benchmark results](@ref timings.png){html: width=50%, latex: width=10cm} diff --git a/doc/content/_05_physics.md b/doc/content/_03_physics.md similarity index 100% rename from doc/content/_05_physics.md rename to doc/content/_03_physics.md diff --git a/doc/content/_04_dev.md b/doc/content/_04_dev.md new file mode 100644 index 0000000000..fdc101b6b6 --- /dev/null +++ b/doc/content/_04_dev.md @@ -0,0 +1,244 @@ +# Developer Guide {#dev} + +[TOC] + +## Writing a custom model {#custom-model} + +The following tutorials serve as a developer-facing step-by-step guide for creating and testing a custom material model. A simple linear isotropic hardening is used as the example in this tutorial. The model can be mathematically written as +\f{align*} + k &= H \bar{\varepsilon}^p, +\f} +where \f$\bar{\varepsilon}^p\f$ is the equivalent plastic strain and \f$k\f$ is the isotropic hardening. The input variable for this model is +\f$\mathbf{\varepsilon}\f$, the output variable for this model is \f$k\f$, and the parameters of the model is \f$H\f$. + +### Naming conventions {#naming-conventions} + +Recall that NEML2 models operates on _labeled tensors_, and that the collection of labels (with their corresponding layout) is called an labeled axis ([LabeledAxis](@ref neml2::LabeledAxis)). NEML2 predefines 5 sub-axes to categorize all the input, output and intermediate variables: +- State \f$\mathcal{S}\f$: Variables on the "state" sub-axis collectively characterize the current _state_ of the material subject to given external forces. The state variables are usually the output of a physically meaningful material model. +- Forces \f$\mathcal{F}\f$: Variables on the "forces" sub-axis define the _external_ forces that drive the response of the material. +- Old state \f$\mathcal{S}_n\f$: The state variables _prior to_ the current material update. In the time-discrete setting, these are the state variables from the previous time step. +- Old forces \f$\mathcal{F}_n\f$: The external forces _prior to_ the current material update. In the time-discrete setting, these are the forces from the previous time step. +- Residual \f$\mathcal{R}\f$: The residual defines an _implicit_ model/function. An implicit model is updated by solving for the state variables that result in zero residual. + +In NEML2, the following naming conventions are recommended: +- User-facing variables and option names should be _as descriptive as possible_. For example, the equivalent plastic strain is named "equivalent_plastic_strain". Note that white spaces, quotes, and left slashes are not allowed in the names. Underscores are recommended as an replacement for white spaces. +- Developer-facing variables and option names should use simple alphanumeric symbols. For example, the equivalent plastic strain is named "ep" in consistency with most of the existing literature. +- Developner-facing member variables and option names should use the same alphanumeric symbols. For example, the member variable for the equivalent plastic strain is named `ep`. However, if the member variable is protected or private, it is recommended to prefix it with an underscore, i.e. `_ep`. +- Struct names and class names should use `PascalCase`. +- Function names should use `snake_case`. + +### Declaring variables {#declaring-variables} + +The development of every model begins with the declaration and registration of its input and output variables. Here, we first define an abstract base class that will be later used to define the linear isotropic hardening relation. The abstract base class defines the isotropic hardening relation +\f[ + k = f\left( \bar{\varepsilon}^p \right), +\f] +mapping the equivalent plastic strain to the isotropic hardening. The base class is named `IsotropicHardening` following the [naming conventions](@ref naming-conventions). The header file [IsotropicHardening.h](@ref neml2::IsotropicHardening) is displayed below +```cpp +#pragma once + +#include "neml2/models/Model.h" + +namespace neml2 +{ +class IsotropicHardening : public Model +{ +public: + static OptionSet expected_options(); + + IsotropicHardening(const OptionSet & options); + +protected: + /// Equivalent plastic strain + const Variable & _ep; + + /// Isotropic hardening + Variable & _h; +}; +} // namespace neml2 +``` +Since isotropic hardening _is a_ model, the class inherits from `Model`. The user-facing expected options are defined by the static method `expected_options`. NEML2 handles the parsing of user-specified options and pass them to the constructor. The input variable of the model is the equivalent plastic strain, and the output variable of the model is the isotropic hardening. Their corresponding variable value references are stored as `_ep` and `_h`, respectively, again following the [naming conventions](@ref naming-conventions). + +The expected options and the constructor are defined as +```cpp +#include "neml2/models/solid_mechanics/IsotropicHardening.h" + +namespace neml2 +{ +OptionSet +IsotropicHardening::expected_options() +{ + OptionSet options = Model::expected_options(); + options.set("equivalent_plastic_strain") = VariableName("state", "internal", "ep"); + options.set("isotropic_hardening") = VariableName("state", "internal", "k"); + return options; +} + +IsotropicHardening::IsotropicHardening(const OptionSet & options) + : Model(options), + _ep(declare_input_variable("equivalent_plastic_strain")), + _h(declare_output_variable("isotropic_hardening")) +{ +} +} // namespace neml2 +``` +Recall that variable names on `LabeledAxis` are always fully qualified, the equivalent plastic strain and the isotropic hardening are denoted as "state/internal/ep" and "state/internal/k", respectively. An instance of the class is constructed by extracting user-specified options (of type `OptionSet`). Note how `declare_input_variable` and `declare_output_variable` are used to declare and register the input and output variables. + +### Declaring parameters {#declaring-parameters} + +Now that the abstract base class `IsotropicHardening` has been implemented, we are ready to define our first concrete NEML2 model that describes a linear isotropic hardening relation +\f[ + k = H \bar{\varepsilon}^p. +\f] +Note that \f$H\f$ is a model parameter. Following the naming convention, the concrete class is named `LinearIsotropicHardening`. The header file is displayed below. +```cpp +#pragma once + +#include "neml2/models/solid_mechanics/IsotropicHardening.h" + +namespace neml2 +{ +/** + * @brief Simple linear map between equivalent strain and hardening + * + */ +class LinearIsotropicHardening : public IsotropicHardening +{ +public: + static OptionSet expected_options(); + + LinearIsotropicHardening(const OptionSet & options); + +protected: + void set_value(bool out, bool dout_din, bool d2out_din2) override; + + const Scalar & _K; +}; +} // namespace neml2 +``` +It derives from the abstract base class `IsotropicHardening` and implements the method `set_value` as the forward operator. The model parameter \f$H\f$ is stored as a protected member variable `_K`. The model implementation is shown below. +```cpp +#include "neml2/models/solid_mechanics/LinearIsotropicHardening.h" + +namespace neml2 +{ +register_NEML2_object(LinearIsotropicHardening); + +OptionSet +LinearIsotropicHardening::expected_options() +{ + OptionSet options = IsotropicHardening::expected_options(); + options.set>("hardening_modulus"); + return options; +} + +LinearIsotropicHardening::LinearIsotropicHardening(const OptionSet & options) + : IsotropicHardening(options), + _K(declare_parameter("K", "hardening_modulus")) +{ +} + +void +LinearIsotropicHardening::set_value(bool out, bool dout_din, bool d2out_din2) +{ + if (out) + _h = _K * _ep; + + if (dout_din) + _h.d(_ep) = _K; + + if (d2out_din2) + { + // zero + } +} +} // namespace neml2 +``` +Note that an additional option named "hardening_modulus" is requested from the user. The model parameter is registered using the API `declare_parameter`. In the `set_value` method, the current value of the input variable, equivalent plastic strain, is stored in the member `_ep`, and so the isotropic hardening can be computed as +```cpp +_K * _ep +``` +The computed result is copied into the model output variable `_h` by +```cpp +_h = _K * _ep; +``` +In addition, the first derivative of the forward operator is defined as +```cpp +_h.d(_ep) = _K; +``` +Last but not the least, the model is registed in the NEML2 model factory using the macro +```cpp +register_NEML2_object(LinearIsotropicHardening); +``` +so that an instance of the class can be created at runtime. + +## Testing {#testing} + +It is of paramount importance to ensure the correctness of the implementation. NEML2 offers 5 types of tests with different purposes. + +### Catch tests {#catch-tests} + +A Catch test refers to a test directly written in C++ source code within the Catch2 framework. It offers the highest level of flexibility, but requires more effort to set up. To understand how a Catch2 test works, please refer to the [official Catch2 documentation](https://github.com/catchorg/Catch2/blob/v2.x/docs/tutorial.md). + +### Unit tests {#unit-tests} + +A model unit test examines the outputs of a `Model` given a predefined set of inputs. Model unit tests can be directly designed using the input file syntax with the `ModelUnitTest` type. A variety of checks can be turned on and off based on input file options. To list a few: `check_first_derivatives` compares the implemented first order derivatives of the model against finite-differencing results, and the test is marked as passing only if the two derivatives are within tolerances specified with `derivative_abs_tol` and `derivative_rel_tol`; if `check_cuda` is set to `true`, all checks are repeated twice, once on CPU and once on GPU (if available), and pass only if the two evaluations yield same results within tolerances. + +All input files for model unit tests should be stored inside `tests/unit/models`. Every input file with the `.i` extension will be automatically discovered and executed. To run all the model unit tests, use the following commands +``` +cd tests +./unit_tests models +``` + +To run a specific model unit test, use the `-c` command line option followed by the relative location of the input file, i.e. +``` +cd tests +./unit_tests models -c solid_mechanics/LinearIsotropicElasticity.i +``` + +### Regression tests {#regression-tests} + +A model regression test runs a `Model` using a user specified driver. The results are compared against a predefined reference (stored on the disk checked into the repository). The test passes only if the current results are the same as the predefined reference (again within specified tolerances). The regression tests ensure the consistency of implementations across commits. Currently, `TransientRegression` is the only supported type of regression test. + +Each input file for model regression tests should be stored inside a separate folder inside `tests/regression`. Every input file with the `.i` extension will be automatically discovered and executed. To run all the model regression tests, use the `regression_tests` executable followed by the physics module, i.e. +``` +cd tests +./regression_tests "solid mechanics" +``` +To run a specific model regression test, use the `-c` command line option followed by the relative location of the input file, i.e. +``` +cd tests +./regression_tests "solid mechanics" -c viscoplasticity/chaboche/model.i +``` +Note that the regression test expects an option `reference` which specifies the relative location to the reference solution. + +### Verification tests {#verification-tests} + +The model verification test is similar to the model regression test in terms of workflow. The difference is the a verification test defines the reference solution using NEML, the predecessor of NEML2. Since NEML was developed with strict software assurance, the verification tests ensure that the migration from NEML to NEML2 does not cause any regression in software quality. + +Each input file for model verification tests should be stored inside a separate folder inside `tests/verification`. Every input file with the `.i` extension will be automatically discovered and executed. To run all the model verification tests, use the `verification_tests` executable followed by the physics module, i.e. +``` +cd tests +./verification_tests "solid mechanics" +``` + +To run a specific model verification test, use the `-c` command line option followed by the relative location of the input file, i.e. +``` +cd tests +./verification_tests "solid mechanics" -c chaboche/chaboche.i +``` +The regression test compares variables (specified using the `variables` option) against reference values (specified using the `references` option). The reference variables can be read using input objects with type `VTestTimeSeries`. + +### Benchmarking {#benchmarking} + +The benchmark tests can be authored within the [Catch2 microbenchmarking framework](https://github.com/catchorg/Catch2/blob/v2.x/docs/benchmarks.md). Before any benchmarks can be executed, the clock's resolution is estimated. A few other environmental artifacts are also estimated at this point, like the cost of calling the clock function, but they almost never have any impact in the results. The user code is executed a few times to obtain an estimate of the amount of runs that should be in each sample. This also has the potential effect of bringing relevant code and data into the caches before the actual measurement starts. Finally, all the samples are collected sequentially by performing the number of runs estimated in the previous step for each sample. + +To run a benchmark test, use the `benchmark.sh` script inside the `scripts` directory with 3 positional arguments: +``` +./scripts/benchmark.sh Chaboche 5 timings +``` +The first positional argument specifies the name of the benchmark test to run. The second positional argument specifies the number of samples to repeat in each iteration. The third positional argument specifies the output directory of the benchmark results. + +The Chaboche benchmark test is repeated with different batch sizes and on different devices (in this case CPU and GPU). The final benchmark results are summarized in the following figure. + +![Chaboche benchmark results](@ref timings.png){html: width=50%, latex: width=10cm} diff --git a/doc/content/_04_system.md b/doc/content/_04_system.md deleted file mode 100644 index 4ea7af8b57..0000000000 --- a/doc/content/_04_system.md +++ /dev/null @@ -1,3 +0,0 @@ -# System Documentation {#system} - -[TOC] From fdc0248df407479edbd011583cd11c607b7dea2d Mon Sep 17 00:00:00 2001 From: Gary Hu Date: Tue, 7 May 2024 14:27:51 -0500 Subject: [PATCH 15/55] Reorganization and more docs --- README.md | 2 +- doc/CMakeLists.txt | 2 +- doc/config/DoxygenLayout.xml | 8 ++- doc/content/_02_02_data.md | 3 -- doc/content/_02_04_solver.md | 3 -- doc/content/_02_05_driver.md | 3 -- doc/content/_02_system.md | 11 ---- doc/content/_03_physics.md | 3 -- doc/content/{_04_dev.md => dev.md} | 3 +- doc/content/getting_started.md | 54 +++++++++++++++++++ doc/content/{_01_install.md => install.md} | 0 doc/content/physics/index.md | 5 ++ doc/content/physics/solid_mechanics.md | 1 + doc/content/system/data.md | 21 ++++++++ doc/content/system/driver.md | 7 +++ doc/content/system/index.md | 13 +++++ .../{_02_03_model.md => system/model.md} | 6 +++ doc/content/system/solver.md | 47 ++++++++++++++++ .../{_02_01_tensor.md => system/tensor.md} | 0 scripts/syntax_to_md.py | 8 +-- 20 files changed, 168 insertions(+), 32 deletions(-) delete mode 100644 doc/content/_02_02_data.md delete mode 100644 doc/content/_02_04_solver.md delete mode 100644 doc/content/_02_05_driver.md delete mode 100644 doc/content/_02_system.md delete mode 100644 doc/content/_03_physics.md rename doc/content/{_04_dev.md => dev.md} (96%) create mode 100644 doc/content/getting_started.md rename doc/content/{_01_install.md => install.md} (100%) create mode 100644 doc/content/physics/index.md create mode 100644 doc/content/physics/solid_mechanics.md create mode 100644 doc/content/system/data.md create mode 100644 doc/content/system/driver.md create mode 100644 doc/content/system/index.md rename doc/content/{_02_03_model.md => system/model.md} (92%) create mode 100644 doc/content/system/solver.md rename doc/content/{_02_01_tensor.md => system/tensor.md} (100%) diff --git a/README.md b/README.md index ea170f549b..ff5962599e 100644 --- a/README.md +++ b/README.md @@ -1,4 +1,4 @@ -# Overview +# Overview {#overview} [![Documentation](https://github.com/reverendbedford/neml2/actions/workflows/build_docs.yml/badge.svg?branch=main)](https://reverendbedford.github.io/neml2/) [![tests](https://github.com/reverendbedford/neml2/actions/workflows/tests.yml/badge.svg?branch=main)](https://github.com/reverendbedford/neml2/actions/workflows/tests.yml) diff --git a/doc/CMakeLists.txt b/doc/CMakeLists.txt index 0c14934d67..c9f97b9d3f 100644 --- a/doc/CMakeLists.txt +++ b/doc/CMakeLists.txt @@ -54,7 +54,7 @@ add_custom_target(doc-syntax DEPENDS syntax WORKING_DIRECTORY ${NEML2_BINARY_DIR}/doc COMMAND ${NEML2_BINARY_DIR}/doc/syntax - COMMAND ${Python_EXECUTABLE} ${NEML2_SOURCE_DIR}/scripts/syntax_to_md.py syntax.yml content/_99_syntax.md + COMMAND ${Python_EXECUTABLE} ${NEML2_SOURCE_DIR}/scripts/syntax_to_md.py syntax.yml content/syntax.md COMMENT "Generate NEML2 syntax and convert to markdown" VERBATIM ) diff --git a/doc/config/DoxygenLayout.xml b/doc/config/DoxygenLayout.xml index 23c1fecaf0..c10dee3ff9 100644 --- a/doc/config/DoxygenLayout.xml +++ b/doc/config/DoxygenLayout.xml @@ -3,14 +3,18 @@ + + - + - + + + diff --git a/doc/content/_02_02_data.md b/doc/content/_02_02_data.md deleted file mode 100644 index 7b499cfa1d..0000000000 --- a/doc/content/_02_02_data.md +++ /dev/null @@ -1,3 +0,0 @@ -# Data {#data} - -[TOC] diff --git a/doc/content/_02_04_solver.md b/doc/content/_02_04_solver.md deleted file mode 100644 index c4b949c682..0000000000 --- a/doc/content/_02_04_solver.md +++ /dev/null @@ -1,3 +0,0 @@ -# Solver {#solver} - -[TOC] diff --git a/doc/content/_02_05_driver.md b/doc/content/_02_05_driver.md deleted file mode 100644 index 18fbfb3582..0000000000 --- a/doc/content/_02_05_driver.md +++ /dev/null @@ -1,3 +0,0 @@ -# Driver {#driver} - -[TOC] diff --git a/doc/content/_02_system.md b/doc/content/_02_system.md deleted file mode 100644 index 8b914ca99e..0000000000 --- a/doc/content/_02_system.md +++ /dev/null @@ -1,11 +0,0 @@ -# System Documentation {#system} - -| System | Input file entry point | Description | -| :-------------------- | :--------------------- | :------------------------------------------ | -| [Tensor](@ref tensor) | `[Tensors]` | Tensors created at run-time | -| [Data](@ref data) | `[Data]` | Data used by models | -| [Model](@ref model) | `[Models]` | The model or the composition of models | -| [Solver](@ref solver) | `[Solvers]` | Solvers used to solve a system of equations | -| [Driver](@ref driver) | `[Drivers]` | Predefined drivers to run models | - -Follow the above links for for in-depth explanation of each system. diff --git a/doc/content/_03_physics.md b/doc/content/_03_physics.md deleted file mode 100644 index bee2cd0742..0000000000 --- a/doc/content/_03_physics.md +++ /dev/null @@ -1,3 +0,0 @@ -# Physics Documentation {#physics} - -[TOC] diff --git a/doc/content/_04_dev.md b/doc/content/dev.md similarity index 96% rename from doc/content/_04_dev.md rename to doc/content/dev.md index fdc101b6b6..478a35e39f 100644 --- a/doc/content/_04_dev.md +++ b/doc/content/dev.md @@ -57,7 +57,7 @@ protected: }; } // namespace neml2 ``` -Since isotropic hardening _is a_ model, the class inherits from `Model`. The user-facing expected options are defined by the static method `expected_options`. NEML2 handles the parsing of user-specified options and pass them to the constructor. The input variable of the model is the equivalent plastic strain, and the output variable of the model is the isotropic hardening. Their corresponding variable value references are stored as `_ep` and `_h`, respectively, again following the [naming conventions](@ref naming-conventions). +Since isotropic hardening _is a_ model, the class inherits from `Model`. The user-facing expected options are defined by the static method `expected_options`. NEML2 handles the parsing of user-specified options and pass them to the constructor (see [Input file syntax](@ref input-file-syntax) on how the input file works). The input variable of the model is the equivalent plastic strain, and the output variable of the model is the isotropic hardening. Their corresponding variable value references are stored as `_ep` and `_h`, respectively, again following the [naming conventions](@ref naming-conventions). The expected options and the constructor are defined as ```cpp @@ -242,3 +242,4 @@ The first positional argument specifies the name of the benchmark test to run. T The Chaboche benchmark test is repeated with different batch sizes and on different devices (in this case CPU and GPU). The final benchmark results are summarized in the following figure. ![Chaboche benchmark results](@ref timings.png){html: width=50%, latex: width=10cm} + diff --git a/doc/content/getting_started.md b/doc/content/getting_started.md new file mode 100644 index 0000000000..8b53f1921c --- /dev/null +++ b/doc/content/getting_started.md @@ -0,0 +1,54 @@ +# Getting Started {#getting-started} + +[TOC] + +## Using NEML2 material models + +The user interface of NEML2 is designed in such a way that no programing experience is required to compose custom material models and define how they are solved. This is achieved using _input files_. The input files are simply text files with a specific format that NEML2 can understand. NEML2 can _deserialize_ an input file, i.e., parse and create material models specified within the input file. + +Since the input files are nothing more than text files saved on the disk, they can be used in any application that supports standard IO, easily exchanged among different devices running different operating systems, and archived for future reference. + +## Input file syntax {#input-file-syntax} + +Input files use the Hierarchical Input Text (HIT) format. The syntax looks like this: +```python +# Comments look like this +[block1] + # This is a comment + foo = 1 + bar = 3.14159 + baz = 'string value' + [nested_block] + # ... + [] +[] +``` +where key-value pairs are defined under (nested) blocks denoted by square brackets. A value can be an integer, floating-point number, string, or array (as indicated by single quotes). Note that the block indentation is recommended for clarity but is not required. + +All NEML2 capabilities that can be defined through the input file fall under a number of _systems_. Names of the top-level blocks specify the systems. For example, the following input file +```python +[Tensors] + [E] + # ... + [] +[] + +[Models] + [elasticity] + type = LinearIsotropicElasticity + youngs_modulus = 'E' + poisson_ratio = 0.3 + [] +[] +``` +defines a tensor named "E" under the `[Tensors]` block and a model named "elasticity" under the `[Models]` block. The [Syntax Documentation](@ref syntax) provides a complete list of objects that can be defined by an input file. The [System Documentation](@ref system) provides detailed explanation of each system. + +## Special syntax + +**Boolean**: Oftentimes the behavior of the object is preferrably controlled by a boolean flag. However, since the HIT format only allows (array of) integer, floating-point number, and string, a special syntax shall be reserved for boolean values. In NEML2 input files, a string with value "true" can be parsed into a boolean `true`, and a string with value "false" can be parsed into a boolean `false`. + +> On the other hand, other commonly used boolean flags such as "on"/"off", "1"/"0", "True"/"False", etc., cannot be parsed into boolean values. Trying to do so will trigger a `ParserException`. + +**Variable name**: NEML2 material models work with named variables to assign physical meanings to different slices of a tensor (see e.g. [Tensor Labeling](@ref tensor-labeling)). A fully qualified variable name can be parsed from a string, and the delimiter "/" signifies nested sub-axes. For example, the string "forces/t" can be parsed into a variable named "t" defined on the sub-axis named "forces". + +**Tensor shape**: Shape of a tensor can also be parsed from a string. The string must start with "(" and end with ")". An array of comma-separated integers must be enclosed by the parentheses. For example, the string "(5,6,7)" can be parsed into a shape tuple of value `(5, 6, 7)`. Note that white spaces are not allowed between the parentheses and could lead to undefined behavior. An empty array, i.e. "()", however, is allowed and fully supported. diff --git a/doc/content/_01_install.md b/doc/content/install.md similarity index 100% rename from doc/content/_01_install.md rename to doc/content/install.md diff --git a/doc/content/physics/index.md b/doc/content/physics/index.md new file mode 100644 index 0000000000..182bd93a4c --- /dev/null +++ b/doc/content/physics/index.md @@ -0,0 +1,5 @@ +# Physics Documentation {#physics} + +| Physics module | Description | +| :-------------------------------------- | :-------------------------------------------------------- | +| [Solid mechanics](@ref solid-mechanics) | Elastic, plastic, and viscous behavior of solid materials | diff --git a/doc/content/physics/solid_mechanics.md b/doc/content/physics/solid_mechanics.md new file mode 100644 index 0000000000..75e3274a5e --- /dev/null +++ b/doc/content/physics/solid_mechanics.md @@ -0,0 +1 @@ +# Solid Mechanics {#solid-mechanics} diff --git a/doc/content/system/data.md b/doc/content/system/data.md new file mode 100644 index 0000000000..a1d83e4b6c --- /dev/null +++ b/doc/content/system/data.md @@ -0,0 +1,21 @@ +# Data {#data} + +[TOC] + +## Buffer + +The term "buffer" is inherited from PyTorch. In a `torch.nn.Module` (which can be thought of the PyTorch equivalent of NEML2's `Model`), a set of _parameters_ and _buffer_ can be declared. Both parameters and buffer are tensors registered to the model. When the model is sent to a different device, i.e., from CPU to GPU, the parameters and buffer registered with the model are sent to the target device. + +The only notable difference between buffer and parameter is that buffer tensors are _NOT_ meant to part of the function graph (while calling the model's forward operator), while parameters are expected to be differentiated. Some examples of buffer, in the context of crystal plasticity, include crystal class, lattice vectors, slip planes, and slip directions of a crystal. + +## Data + +NEML2 provides the Data system to predefine commonly used buffer tensors that are oftentimes shared among material models. Data objects shall inherit from the base class `Data` and can register as many buffer tensors as necessary. + +Objects that are defined as part of the Data system are different from models in several ways: +1. Data objects _do not_ contain parameters. +2. Data objects _do not_ define variables. +3. Data objects _do not_ define forward operator. +4. Data objects _do not_ participate in dependency resolution. + +A model can register a Data object using the `register_data` method which returns a reference to the registered `Data`. diff --git a/doc/content/system/driver.md b/doc/content/system/driver.md new file mode 100644 index 0000000000..d6c506ebe5 --- /dev/null +++ b/doc/content/system/driver.md @@ -0,0 +1,7 @@ +# Driver {#driver} + +[TOC] + +Drivers are objects that "drive" the update and evolution of one or more material models and their internal data. Especially for non-autonomous material models, a driver is mandatory to evolve the mateiral model. + +Drivers must derive from the base class `Driver` and override the pure virtual method `run` which returns a boolean indicating whether the model execution was successful. diff --git a/doc/content/system/index.md b/doc/content/system/index.md new file mode 100644 index 0000000000..38eb445495 --- /dev/null +++ b/doc/content/system/index.md @@ -0,0 +1,13 @@ +# System Documentation {#system} + +| System | Input file entry point | Description | Objects | +| :-------------------- | :--------------------- | :------------------------------------------ | :-------------------------- | +| [Tensor](@ref tensor) | `[Tensors]` | Tensors created at run-time | [Link](@ref syntax-tensors) | +| [Model](@ref model) | `[Models]` | The model or the composition of models | [Link](@ref syntax-models) | +| [Solver](@ref solver) | `[Solvers]` | Solvers used to solve a system of equations | [Link](@ref syntax-solvers) | +| [Data](@ref data) | `[Data]` | Data used by models | [Link](@ref syntax-data) | +| [Driver](@ref driver) | `[Drivers]` | Predefined drivers to run models | [Link](@ref syntax-drivers) | + +Follow the links in the first column for in-depth explanation of each system. + +Follow the links in the last column for lists of objects from each system that can be defined inside input files. diff --git a/doc/content/_02_03_model.md b/doc/content/system/model.md similarity index 92% rename from doc/content/_02_03_model.md rename to doc/content/system/model.md index 90ab88e26f..5a32796e76 100644 --- a/doc/content/_02_03_model.md +++ b/doc/content/system/model.md @@ -104,3 +104,9 @@ and `Model` \f$h\f$ only defines The assembly of the partial derivatives into the total derivative \f$\partial y / \partial \mathbf{x}\f$ using the chain rule is handled by NEML2. This design serves as the fundation for a modular model implementation: - Each model _does not_ need to know its composition with others. - The same model partial derivatives can be reused in _any_ composition. + +## Automatic differentiation {#automatic-differentiation} + +Deriving and implementing derivatives of the forward operator can be cubersome from times to times. NEML2 offers the option to use automatic differentiation to obtain derivatives. To enable automatic differentiation, simply set the `use_AD_first_derivative` and/or the `use_AD_second_derivative` options to `true`. + +Since a composed model uses chain rule to efficiently evaluate the total derivatives, automatic differentiation is disabled for `ComposedModel`. However, each of the child model can still use AD to calculate the derivatives of its own forward operator. Moreover, AD and non-AD models can be composed together. diff --git a/doc/content/system/solver.md b/doc/content/system/solver.md new file mode 100644 index 0000000000..5ffde73ddf --- /dev/null +++ b/doc/content/system/solver.md @@ -0,0 +1,47 @@ +# Solver {#solver} + +[TOC] + +Many material models are _implicit_, meaning that the update of the material model is the solution to one or more nonlinear systems of equations. While a model or a composition of models can define such nonlinear system, a solver is required to actually _solve_ the system. + +## Nonlinear solver + +All nonlinear solvers derive from the common base class `NonlinearSolver`. The base class defines 3 public members: `atol` for the absolute tolerance, `rtol` for the relative tolerance, and `miters` for the maximum number of iterations. + +Derived classes must override the method +```cpp +std::tuple solve(NonlinearSystem & system, BatchTensor & sol) +``` +The first argument is the nonlinear system of equations to be solved, and the second argument is the initial guess which will be iteratively updated during the solve. The second argument will hold the solution to the system upon convergence. The first tuple element in the return value is a boolean indicating whether the solve has succeeeded, and the second tuple element is the number of iterations taken before convergence. + +While the convergence criteria are defined by the specific solvers derived from the base class, it is generally recommended to use both `atol` and `rtol` in the convergence check. Below is an example convergence criteria +```cpp +bool +MySolver::converged(const torch::Tensor & nR, const torch::Tensor & nR0) const +{ + return torch::all(torch::logical_or(nR < atol, nR / nR0 < rtol)).item(); +} +``` +where `nR` is the vector norm of the current residual, and `nR0` is the vector norm of the initial residual (evaluated at the initial guess). The above statement makes sure the current residual is either below the absolute tolerance or has been sufficiently reduced, and the condition is applied to _all_ batches of the residual norm. + +## Nonlinear system + +The first argument passed to the `NonlinearSolver::solve` method is of type `NonlinearSystem &`. Since `Model` derives from `NonlinearSystem`, no special action needs to be taken to cast a `Model` into a `NonlinearSystem`. However, the input and output axes of the `Model` must conform with the following requirements in order to be properly recognized as a nonlinear system: +1. The input axis must have a sub-axis named "state". +2. The output axis must have a sub-axis named "residual". +3. The input "state" sub-axis and the output "residual" sub-axis must be conformal, i.e., the variable names on the two axes must have one-to-one correspondence. + +With these requirements, the following rules are implied during the evaluation of a nonlinear system: +1. The input "state" sub-axis is used as the initial guess. The driver is responsible for setting the appropriate initial guess. +2. The output "residual" sub-axis is the residual of the nonlinear system. +3. The derivative sub-block "(residual, state)" is the Jacobian of the nonlinear system. + +Since the input "state" sub-axis and the output "residual" sub-axis are required to be conformal, the Jacobian of the nonlinear system must be square (while not necessarily symmetric). + +## Automatic scaling + +In addition to the default direct (LU) solver, iterative solvers, e.g. CG, GMRES, etc., can be used to solve the linearized system associated with each nonlinear update. It is well known that the effectiveness of iterative solvers is affected by the conditioning of the linear system. + +Since NEML2 supports general Multiphysics material models, conditioning of the system cannot be guaranteed directly by the model's forward operator. Therefore, NEML2 provides a mechanism to (attempt to) scale the nonlinear system and reduce the condition number. + +The automatic scaling algorithm used by NEML2 is algebraic and is based on the initial Jacobian (i.e., the Jacobian of the nonlinear system evaluated at its initial condition and the initial guess). The implementation closely follows section 2.3 in this [report](https://cs.stanford.edu/people/paulliu/files/cs517-project.pdf). Note that the resulting scaling matrices are _not_ necessarily a global minimizer, nor a local minimizer, of the condition number. In fact, the condition number isn't guaranteed to decrease. However, in most of the material models that have been tested, this algorithm can substantially improve the conditioning of the system. diff --git a/doc/content/_02_01_tensor.md b/doc/content/system/tensor.md similarity index 100% rename from doc/content/_02_01_tensor.md rename to doc/content/system/tensor.md diff --git a/scripts/syntax_to_md.py b/scripts/syntax_to_md.py index 419cbcfd49..41347c7847 100755 --- a/scripts/syntax_to_md.py +++ b/scripts/syntax_to_md.py @@ -68,7 +68,9 @@ def get_sections(syntax): stream.write("[TOC]\n\n") for section in sections: - stream.write("## [{}]\n\n".format(section)) + stream.write( + "## [{}] {{#{}}}\n\n".format(section, "syntax-" + section.lower()) + ) for type, params in syntax.items(): if params["section"] != section: continue @@ -99,6 +101,4 @@ def get_sections(syntax): if param_value: stream.write(" - Default: {}\n".format(param_value)) stream.write("\n") - stream.write( - "Detailed Doxygen documentation [link](@ref {})\n\n".format(type) - ) + stream.write("Detailed documentation [link](@ref {})\n\n".format(type)) From 64d2f515a00971791018d89a35355827e1ad8ef6 Mon Sep 17 00:00:00 2001 From: Gary Hu Date: Tue, 7 May 2024 14:36:07 -0500 Subject: [PATCH 16/55] Try previewing ghpages in PRs --- .github/workflows/build_docs.yml | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/.github/workflows/build_docs.yml b/.github/workflows/build_docs.yml index cadacd55cf..6fb0672426 100644 --- a/.github/workflows/build_docs.yml +++ b/.github/workflows/build_docs.yml @@ -40,10 +40,16 @@ jobs: - run: make doc-syntax -j 2 - run: make doc-html - run: cat doc/doxygen.log + - name: Preview GitHub Pages + if: ${{ github.event_name == 'pull_request' }} + uses: rossjrw/pr-preview-action@v1 + with: + source-dir: doc/build/html - name: Deploy to GitHub Pages if: ${{ github.event_name == 'push' }} uses: JamesIves/github-pages-deploy-action@v4.4.1 with: branch: gh-pages folder: doc/build/html - single-commit: true + clean-exclude: pr-preview/ + force: false From c340733e57d066aa9a4c540ad9c35b20be01fa9f Mon Sep 17 00:00:00 2001 From: Gary Hu Date: Tue, 7 May 2024 14:38:33 -0500 Subject: [PATCH 17/55] No need to build unit tests anymore for the docs targets --- .github/workflows/build_docs.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/build_docs.yml b/.github/workflows/build_docs.yml index 6fb0672426..d59bfd5cf3 100644 --- a/.github/workflows/build_docs.yml +++ b/.github/workflows/build_docs.yml @@ -28,8 +28,8 @@ jobs: - run: | cmake \ -DCMAKE_BUILD_TYPE=Release \ - -DCMAKE_UNITY_BUILD=OFF \ - -DNEML2_UNIT=ON \ + -DCMAKE_UNITY_BUILD=ON \ + -DNEML2_UNIT=OFF \ -DNEML2_REGRESSION=OFF \ -DNEML2_VERIFICATION=OFF \ -DNEML2_BENCHMARK=OFF \ From 5db15f915233f47ac0a69bbc0a1d9720ecceaa42 Mon Sep 17 00:00:00 2001 From: Gary Hu Date: Tue, 7 May 2024 14:43:54 -0500 Subject: [PATCH 18/55] Allow the PR ghpage preview to cleanup after itself --- .github/workflows/build_docs.yml | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/.github/workflows/build_docs.yml b/.github/workflows/build_docs.yml index d59bfd5cf3..1e4993b059 100644 --- a/.github/workflows/build_docs.yml +++ b/.github/workflows/build_docs.yml @@ -6,6 +6,11 @@ on: branches: [main] pull_request: branches: [main] + types: + - opened + - reopened + - synchronize + - closed # This is important for the ghpage preview to clean up after itself # Allows you to run this workflow manually from the Actions tab workflow_dispatch: From be022f7d3ae44be2250315ce67498a240cc04a23 Mon Sep 17 00:00:00 2001 From: Gary Hu Date: Tue, 14 May 2024 09:40:15 -0500 Subject: [PATCH 19/55] Suppress a few parameters to make syntax documentation less clustered --- doc/content/system/model.md | 2 +- include/neml2/solvers/NonlinearSystem.h | 4 ++++ .../models/BackwardEulerTimeIntegration.cxx | 1 + src/neml2/models/ComposedModel.cxx | 5 +++-- src/neml2/models/Model.cxx | 11 +++++++---- .../WR2ImplicitExponentialTimeIntegration.cxx | 1 + src/neml2/solvers/NonlinearSystem.cxx | 16 ++++++++++++++++ .../viscoplasticity/misc/polynomial/model.i | 2 -- .../viscoplasticity/misc/torch_script/model.i | 2 -- tests/src/TabulatedPolynomialModel.cxx | 3 +++ tests/src/TorchScriptFlowRate.cxx | 3 +++ tests/unit/base/test_HITParser.cxx | 4 ++-- 12 files changed, 41 insertions(+), 13 deletions(-) diff --git a/doc/content/system/model.md b/doc/content/system/model.md index 5a32796e76..716dd24ebe 100644 --- a/doc/content/system/model.md +++ b/doc/content/system/model.md @@ -107,6 +107,6 @@ The assembly of the partial derivatives into the total derivative \f$\partial y ## Automatic differentiation {#automatic-differentiation} -Deriving and implementing derivatives of the forward operator can be cubersome from times to times. NEML2 offers the option to use automatic differentiation to obtain derivatives. To enable automatic differentiation, simply set the `use_AD_first_derivative` and/or the `use_AD_second_derivative` options to `true`. +Deriving and implementing derivatives of the forward operator can be cubersome from times to times. NEML2 offers the option to use automatic differentiation to obtain derivatives. To enable automatic differentiation, simply set the `_use_AD_first_derivative` and/or the `_use_AD_second_derivative` options to `true`. Since a composed model uses chain rule to efficiently evaluate the total derivatives, automatic differentiation is disabled for `ComposedModel`. However, each of the child model can still use AD to calculate the derivatives of its own forward operator. Moreover, AD and non-AD models can be composed together. diff --git a/include/neml2/solvers/NonlinearSystem.h b/include/neml2/solvers/NonlinearSystem.h index a4cd9d0dea..aa61673570 100644 --- a/include/neml2/solvers/NonlinearSystem.h +++ b/include/neml2/solvers/NonlinearSystem.h @@ -38,6 +38,10 @@ class NonlinearSystem public: static OptionSet expected_options(); + static void disable_automatic_scaling(OptionSet & options); + + static void enable_automatic_scaling(OptionSet & options); + NonlinearSystem(const OptionSet & options); /** diff --git a/src/neml2/models/BackwardEulerTimeIntegration.cxx b/src/neml2/models/BackwardEulerTimeIntegration.cxx index bb55b3c730..a34803b0ec 100644 --- a/src/neml2/models/BackwardEulerTimeIntegration.cxx +++ b/src/neml2/models/BackwardEulerTimeIntegration.cxx @@ -35,6 +35,7 @@ OptionSet BackwardEulerTimeIntegration::expected_options() { OptionSet options = Model::expected_options(); + NonlinearSystem::enable_automatic_scaling(options); options.set("variable"); options.set("variable_rate"); options.set("time") = VariableName("t"); diff --git a/src/neml2/models/ComposedModel.cxx b/src/neml2/models/ComposedModel.cxx index a7d83d3027..e4564c1a71 100644 --- a/src/neml2/models/ComposedModel.cxx +++ b/src/neml2/models/ComposedModel.cxx @@ -32,6 +32,7 @@ OptionSet ComposedModel::expected_options() { OptionSet options = Model::expected_options(); + NonlinearSystem::enable_automatic_scaling(options); options.set>("models"); options.set>("additional_outputs"); options.set>("priority"); @@ -98,8 +99,8 @@ ComposedModel::check_AD_limitation() const { if (_AD_1st_deriv || _AD_2nd_deriv) throw NEMLException( - "ComposedModel does not use automatic differentiation. use_AD_first_derivative and " - "use_AD_second_derivative should be set to false."); + "ComposedModel does not use automatic differentiation. _use_AD_first_derivative and " + "_use_AD_second_derivative should be set to false."); } void diff --git a/src/neml2/models/Model.cxx b/src/neml2/models/Model.cxx index 8ca3f355c6..f9a132e4f1 100644 --- a/src/neml2/models/Model.cxx +++ b/src/neml2/models/Model.cxx @@ -33,16 +33,19 @@ Model::expected_options() { OptionSet options = Data::expected_options(); options += NonlinearSystem::expected_options(); + NonlinearSystem::disable_automatic_scaling(options); options.section() = "Models"; - options.set("use_AD_first_derivative") = false; - options.set("use_AD_second_derivative") = false; + options.set("_use_AD_first_derivative") = false; + options.set("_use_AD_second_derivative") = false; options.set("_extra_derivative_order") = 0; options.set("_nonlinear_system") = false; options.set("_extra_derivative_order").suppressed() = true; options.set("_nonlinear_system").suppressed() = true; + options.set("_use_AD_first_derivative").suppressed() = true; + options.set("_use_AD_second_derivative").suppressed() = true; return options; } @@ -52,8 +55,8 @@ Model::Model(const OptionSet & options) ParameterStore(options, this), VariableStore(options, this), NonlinearSystem(options), - _AD_1st_deriv(options.get("use_AD_first_derivative")), - _AD_2nd_deriv(options.get("use_AD_second_derivative")), + _AD_1st_deriv(options.get("_use_AD_first_derivative")), + _AD_2nd_deriv(options.get("_use_AD_second_derivative")), _options(default_tensor_options()), _deriv_order(-1), _extra_deriv_order(options.get("_extra_derivative_order")), diff --git a/src/neml2/models/WR2ImplicitExponentialTimeIntegration.cxx b/src/neml2/models/WR2ImplicitExponentialTimeIntegration.cxx index 8ae1cc86c5..d693b04104 100644 --- a/src/neml2/models/WR2ImplicitExponentialTimeIntegration.cxx +++ b/src/neml2/models/WR2ImplicitExponentialTimeIntegration.cxx @@ -37,6 +37,7 @@ OptionSet WR2ImplicitExponentialTimeIntegration::expected_options() { OptionSet options = Model::expected_options(); + NonlinearSystem::enable_automatic_scaling(options); options.set("variable"); options.set("time") = VariableName("t"); return options; diff --git a/src/neml2/solvers/NonlinearSystem.cxx b/src/neml2/solvers/NonlinearSystem.cxx index d8a35fbd36..b7da978667 100644 --- a/src/neml2/solvers/NonlinearSystem.cxx +++ b/src/neml2/solvers/NonlinearSystem.cxx @@ -37,6 +37,22 @@ NonlinearSystem::expected_options() return options; } +void +NonlinearSystem::disable_automatic_scaling(OptionSet & options) +{ + options.set("automatic_scaling").suppressed() = true; + options.set("automatic_scaling_tol").suppressed() = true; + options.set("automatic_scaling_miter").suppressed() = true; +} + +void +NonlinearSystem::enable_automatic_scaling(OptionSet & options) +{ + options.set("automatic_scaling").suppressed() = false; + options.set("automatic_scaling_tol").suppressed() = false; + options.set("automatic_scaling_miter").suppressed() = false; +} + NonlinearSystem::NonlinearSystem(const OptionSet & options) : _autoscale(options.get("automatic_scaling")), _autoscale_tol(options.get("automatic_scaling_tol")), diff --git a/tests/regression/solid_mechanics/viscoplasticity/misc/polynomial/model.i b/tests/regression/solid_mechanics/viscoplasticity/misc/polynomial/model.i index 2134dc6897..e5cc41ef5f 100644 --- a/tests/regression/solid_mechanics/viscoplasticity/misc/polynomial/model.i +++ b/tests/regression/solid_mechanics/viscoplasticity/misc/polynomial/model.i @@ -208,8 +208,6 @@ stress_tile_upper_bounds = 's_ub' temperature_tile_lower_bounds = 'T_lb' temperature_tile_upper_bounds = 'T_ub' - use_AD_first_derivative = true - use_AD_second_derivative = true [] [integrate_ep] type = ScalarBackwardEulerTimeIntegration diff --git a/tests/regression/solid_mechanics/viscoplasticity/misc/torch_script/model.i b/tests/regression/solid_mechanics/viscoplasticity/misc/torch_script/model.i index cf5bcded45..987bbdedf2 100644 --- a/tests/regression/solid_mechanics/viscoplasticity/misc/torch_script/model.i +++ b/tests/regression/solid_mechanics/viscoplasticity/misc/torch_script/model.i @@ -160,8 +160,6 @@ nbatch = 20 internal_state_1_rate = 'state/G_rate' internal_state_2_rate = 'state/C_rate' torch_script = 'gold/surrogate.pt' - use_AD_first_derivative = true - use_AD_second_derivative = true [] [integrate_ep] type = ScalarBackwardEulerTimeIntegration diff --git a/tests/src/TabulatedPolynomialModel.cxx b/tests/src/TabulatedPolynomialModel.cxx index a42d99f8cf..85a00c011d 100644 --- a/tests/src/TabulatedPolynomialModel.cxx +++ b/tests/src/TabulatedPolynomialModel.cxx @@ -50,6 +50,9 @@ TabulatedPolynomialModel::expected_options() options.set>("temperature_tile_lower_bounds"); options.set>("temperature_tile_upper_bounds"); options.set("index_sharpness") = 1.0; + // Use AD + options.set("_use_AD_first_derivative") = true; + options.set("_use_AD_second_derivative") = true; return options; } diff --git a/tests/src/TorchScriptFlowRate.cxx b/tests/src/TorchScriptFlowRate.cxx index f5b73529bb..7ba98d1ee4 100644 --- a/tests/src/TorchScriptFlowRate.cxx +++ b/tests/src/TorchScriptFlowRate.cxx @@ -43,6 +43,9 @@ TorchScriptFlowRate::expected_options() options.set("internal_state_2_rate") = VariableName("state", "C_rate"); // The machine learning model options.set("torch_script"); + // Use AD + options.set("_use_AD_first_derivative") = true; + options.set("_use_AD_second_derivative") = true; return options; } diff --git a/tests/unit/base/test_HITParser.cxx b/tests/unit/base/test_HITParser.cxx index 4d73b9459a..82a4fce318 100644 --- a/tests/unit/base/test_HITParser.cxx +++ b/tests/unit/base/test_HITParser.cxx @@ -50,8 +50,8 @@ TEST_CASE("HITParser", "[base]") SECTION("default values") { - REQUIRE(options.get("use_AD_first_derivative") == false); - REQUIRE(options.get("use_AD_second_derivative") == false); + REQUIRE(options.get("_use_AD_first_derivative") == false); + REQUIRE(options.get("_use_AD_second_derivative") == false); } SECTION("booleans") From 9c29ec18d6c10a05a70a2d11eec7a6180a6ba2dc Mon Sep 17 00:00:00 2001 From: Gary Hu Date: Tue, 14 May 2024 09:57:43 -0500 Subject: [PATCH 20/55] Make parameters in syntax documentation collapsible --- scripts/syntax_to_md.py | 13 ++++++++++--- 1 file changed, 10 insertions(+), 3 deletions(-) diff --git a/scripts/syntax_to_md.py b/scripts/syntax_to_md.py index 41347c7847..1b68d1faff 100755 --- a/scripts/syntax_to_md.py +++ b/scripts/syntax_to_md.py @@ -94,11 +94,18 @@ def get_sections(syntax): param_type = demangle(info["type"]) param_value = postprocess(info["value"], param_type) - stream.write("- {}\n".format(param_name)) - if info["doc"]: - stream.write(" - Description: {}\n".format(info["doc"])) + stream.write("
\n") + if not info["doc"]: + stream.write(" `{}`\n\n".format(param_name)) + else: + stream.write( + " `{}` {}\n\n".format( + param_name, info["doc"] + ) + ) stream.write(" - Type: {}\n".format(param_type)) if param_value: stream.write(" - Default: {}\n".format(param_value)) + stream.write("
\n") stream.write("\n") stream.write("Detailed documentation [link](@ref {})\n\n".format(type)) From cce393aa0c8ee8a036f1e2cbc3e42a793cfa47ec Mon Sep 17 00:00:00 2001 From: Gary Hu Date: Tue, 14 May 2024 10:07:26 -0500 Subject: [PATCH 21/55] Split syntax documentation by section --- doc/CMakeLists.txt | 2 +- doc/config/DoxygenLayout.xml | 8 +++++++- scripts/syntax_to_md.py | 19 +++++++------------ 3 files changed, 15 insertions(+), 14 deletions(-) diff --git a/doc/CMakeLists.txt b/doc/CMakeLists.txt index c9f97b9d3f..a77031e1bf 100644 --- a/doc/CMakeLists.txt +++ b/doc/CMakeLists.txt @@ -54,7 +54,7 @@ add_custom_target(doc-syntax DEPENDS syntax WORKING_DIRECTORY ${NEML2_BINARY_DIR}/doc COMMAND ${NEML2_BINARY_DIR}/doc/syntax - COMMAND ${Python_EXECUTABLE} ${NEML2_SOURCE_DIR}/scripts/syntax_to_md.py syntax.yml content/syntax.md + COMMAND ${Python_EXECUTABLE} ${NEML2_SOURCE_DIR}/scripts/syntax_to_md.py syntax.yml content/syntax COMMENT "Generate NEML2 syntax and convert to markdown" VERBATIM ) diff --git a/doc/config/DoxygenLayout.xml b/doc/config/DoxygenLayout.xml index c10dee3ff9..4ec44e3139 100644 --- a/doc/config/DoxygenLayout.xml +++ b/doc/config/DoxygenLayout.xml @@ -4,7 +4,13 @@ - + + + + + + + diff --git a/scripts/syntax_to_md.py b/scripts/syntax_to_md.py index 1b68d1faff..af89bb78da 100755 --- a/scripts/syntax_to_md.py +++ b/scripts/syntax_to_md.py @@ -55,29 +55,24 @@ def get_sections(syntax): if __name__ == "__main__": - outfile = Path(sys.argv[2]) - outfile.parent.mkdir(parents=True, exist_ok=True) + outdir = Path(sys.argv[2]) + outdir.mkdir(parents=True, exist_ok=True) with open(sys.argv[1], "r") as stream: syntax = yaml.safe_load(stream) sections = get_sections(syntax) - - with open(sys.argv[2], "w") as stream: - stream.write("# Syntax Documentation {#syntax}\n\n") - stream.write("[TOC]\n\n") - - for section in sections: + for section in sections: + with open((outdir / section.lower()).with_suffix(".md"), "w") as stream: stream.write( - "## [{}] {{#{}}}\n\n".format(section, "syntax-" + section.lower()) + "# [{}] {{#{}}}\n\n".format(section, "syntax-" + section.lower()) ) + stream.write("[TOC]\n\n") for type, params in syntax.items(): if params["section"] != section: continue input_type = demangle(params["type"]["value"]) - stream.write( - "### {} {{#{}}}\n\n".format(input_type, input_type.lower()) - ) + stream.write("## {} {{#{}}}\n\n".format(input_type, input_type.lower())) if params["doc"]: stream.write("_{}_\n".format(params["doc"])) for param_name, info in params.items(): From eb3c70848b5a8349411c9e4e9054948336996b72 Mon Sep 17 00:00:00 2001 From: Gary Hu Date: Tue, 14 May 2024 10:13:45 -0500 Subject: [PATCH 22/55] Say VariableName instead of LabeledAxisAccessor --- scripts/syntax_to_md.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/scripts/syntax_to_md.py b/scripts/syntax_to_md.py index af89bb78da..4c40a86fbb 100755 --- a/scripts/syntax_to_md.py +++ b/scripts/syntax_to_md.py @@ -40,6 +40,8 @@ def demangle(type): type = type.replace("std::", "") type = type.replace("at::", "") type = re.sub("CrossRef<(.+)>", r"\1", type) + type = type.replace("LabeledAxisAccessor", "VariableName") + return type From c6028e187f6dfe6cbfb7dff22992caf32cf14058 Mon Sep 17 00:00:00 2001 From: Gary Hu Date: Tue, 14 May 2024 10:33:02 -0500 Subject: [PATCH 23/55] Cross reference system and syntax documentations --- doc/config/DoxygenLayout.xml | 10 +++++----- doc/content/system/data.md | 4 +++- doc/content/system/driver.md | 4 +++- doc/content/system/index.md | 17 +++++++---------- doc/content/system/model.md | 4 +++- doc/content/system/solver.md | 4 +++- doc/content/system/tensor.md | 4 +++- scripts/syntax_to_md.py | 9 ++++++++- src/neml2/models/ArrheniusParameter.cxx | 10 +++++----- 9 files changed, 40 insertions(+), 26 deletions(-) diff --git a/doc/config/DoxygenLayout.xml b/doc/config/DoxygenLayout.xml index 4ec44e3139..a1113003ce 100644 --- a/doc/config/DoxygenLayout.xml +++ b/doc/config/DoxygenLayout.xml @@ -12,11 +12,11 @@ - - - - - + + + + + diff --git a/doc/content/system/data.md b/doc/content/system/data.md index a1d83e4b6c..a4186c7e25 100644 --- a/doc/content/system/data.md +++ b/doc/content/system/data.md @@ -1,7 +1,9 @@ -# Data {#data} +# Data {#system-data} [TOC] +Refer to [Syntax Documentation](@ref syntax-data) for the list of available objects. + ## Buffer The term "buffer" is inherited from PyTorch. In a `torch.nn.Module` (which can be thought of the PyTorch equivalent of NEML2's `Model`), a set of _parameters_ and _buffer_ can be declared. Both parameters and buffer are tensors registered to the model. When the model is sent to a different device, i.e., from CPU to GPU, the parameters and buffer registered with the model are sent to the target device. diff --git a/doc/content/system/driver.md b/doc/content/system/driver.md index d6c506ebe5..c18db5ef78 100644 --- a/doc/content/system/driver.md +++ b/doc/content/system/driver.md @@ -1,7 +1,9 @@ -# Driver {#driver} +# Driver {#system-drivers} [TOC] +Refer to [Syntax Documentation](@ref syntax-drivers) for the list of available objects. + Drivers are objects that "drive" the update and evolution of one or more material models and their internal data. Especially for non-autonomous material models, a driver is mandatory to evolve the mateiral model. Drivers must derive from the base class `Driver` and override the pure virtual method `run` which returns a boolean indicating whether the model execution was successful. diff --git a/doc/content/system/index.md b/doc/content/system/index.md index 38eb445495..e1476a814a 100644 --- a/doc/content/system/index.md +++ b/doc/content/system/index.md @@ -1,13 +1,10 @@ # System Documentation {#system} -| System | Input file entry point | Description | Objects | -| :-------------------- | :--------------------- | :------------------------------------------ | :-------------------------- | -| [Tensor](@ref tensor) | `[Tensors]` | Tensors created at run-time | [Link](@ref syntax-tensors) | -| [Model](@ref model) | `[Models]` | The model or the composition of models | [Link](@ref syntax-models) | -| [Solver](@ref solver) | `[Solvers]` | Solvers used to solve a system of equations | [Link](@ref syntax-solvers) | -| [Data](@ref data) | `[Data]` | Data used by models | [Link](@ref syntax-data) | -| [Driver](@ref driver) | `[Drivers]` | Predefined drivers to run models | [Link](@ref syntax-drivers) | +| System | Description | +| :---------------------------- | :------------------------------------------ | +| [Tensor](@ref system-tensors) | Tensors created at run-time | +| [Model](@ref system-models) | The model or the composition of models | +| [Solver](@ref system-solvers) | Solvers used to solve a system of equations | +| [Data](@ref system-data) | Data used by models | +| [Driver](@ref system-drivers) | Predefined drivers to run models | -Follow the links in the first column for in-depth explanation of each system. - -Follow the links in the last column for lists of objects from each system that can be defined inside input files. diff --git a/doc/content/system/model.md b/doc/content/system/model.md index 716dd24ebe..9c35b5eda7 100644 --- a/doc/content/system/model.md +++ b/doc/content/system/model.md @@ -1,7 +1,9 @@ -# Model {#model} +# Model {#system-models} [TOC] +Refer to [Syntax Documentation](@ref syntax-models) for the list of available objects. + ## Model definition {#model-definition} A NEML2 model is a function (in the context of mathematics) diff --git a/doc/content/system/solver.md b/doc/content/system/solver.md index 5ffde73ddf..7030fdfc5b 100644 --- a/doc/content/system/solver.md +++ b/doc/content/system/solver.md @@ -1,7 +1,9 @@ -# Solver {#solver} +# Solver {#system-solvers} [TOC] +Refer to [Syntax Documentation](@ref syntax-solvers) for the list of available objects. + Many material models are _implicit_, meaning that the update of the material model is the solution to one or more nonlinear systems of equations. While a model or a composition of models can define such nonlinear system, a solver is required to actually _solve_ the system. ## Nonlinear solver diff --git a/doc/content/system/tensor.md b/doc/content/system/tensor.md index 7ce9e46726..ff716e410f 100644 --- a/doc/content/system/tensor.md +++ b/doc/content/system/tensor.md @@ -1,7 +1,9 @@ -# Tensor {#tensor} +# Tensor {#system-tensors} [TOC] +Refer to [Syntax Documentation](@ref syntax-tensors) for the list of available objects. + ## Tensor types {#tensor-types} Currently, PyTorch is the only supported tensor backend in NEML2. Therefore, all tensor types in NEML2 directly inherit from `torch::Tensor`. In the future, support for other tensor backends may be added, but the public-facing interfaces will remain largely the same. diff --git a/scripts/syntax_to_md.py b/scripts/syntax_to_md.py index 4c40a86fbb..9701ce79d3 100755 --- a/scripts/syntax_to_md.py +++ b/scripts/syntax_to_md.py @@ -70,11 +70,18 @@ def get_sections(syntax): "# [{}] {{#{}}}\n\n".format(section, "syntax-" + section.lower()) ) stream.write("[TOC]\n\n") + stream.write( + "Refer to [Systam Documentation](@ref system-{}) for detailed explanation about this system.\n\n".format( + section.lower() + ) + ) for type, params in syntax.items(): if params["section"] != section: continue input_type = demangle(params["type"]["value"]) - stream.write("## {} {{#{}}}\n\n".format(input_type, input_type.lower())) + stream.write( + "### {} {{#{}}}\n\n".format(input_type, input_type.lower()) + ) if params["doc"]: stream.write("_{}_\n".format(params["doc"])) for param_name, info in params.items(): diff --git a/src/neml2/models/ArrheniusParameter.cxx b/src/neml2/models/ArrheniusParameter.cxx index 0dc59a3235..7d17422379 100644 --- a/src/neml2/models/ArrheniusParameter.cxx +++ b/src/neml2/models/ArrheniusParameter.cxx @@ -33,21 +33,21 @@ ArrheniusParameter::expected_options() { OptionSet options = NonlinearParameter::expected_options(); - options.doc() = "Defines the nonlinear parameter as a function of temperature according to the " + options.doc() = "Define the nonlinear parameter as a function of temperature according to the " "Arrhenius law. The nonlinear parameter is therefore parametrized by the " "reference value and the activation energy."; options.set>("reference_value"); - options.set("reference_value").doc() = "reference value of the parameter"; + options.set("reference_value").doc() = "Reference value of the parameter"; options.set>("activation_energy"); - options.set("activation_energy").doc() = "activation energy in the Arrhenius law"; + options.set("activation_energy").doc() = "Activation energy in the Arrhenius law"; options.set("ideal_gas_constant"); - options.set("ideal_gas_constant").doc() = "the ideal gas constant"; + options.set("ideal_gas_constant").doc() = "The ideal gas constant"; options.set("temperature") = VariableName("forces", "T"); - options.set("temperature").doc() = "variable name for the temperature"; + options.set("temperature").doc() = "Variable name for the temperature"; return options; } From be75a5750b4a342f8415a1d1690c5bddc9f88482 Mon Sep 17 00:00:00 2001 From: Gary Hu Date: Tue, 14 May 2024 13:50:00 -0500 Subject: [PATCH 24/55] Document the solid mechanics physics module --- doc/config/Doxyfile.in | 2 +- doc/config/DoxygenLayout.xml | 4 +- doc/config/HTML.in | 2 +- doc/content/physics/elasticity.md | 414 +++++++++++++++++++++++++ doc/content/physics/index.md | 5 - doc/content/physics/solid_mechanics.md | 1 - doc/content/system/index.md | 10 - 7 files changed, 418 insertions(+), 20 deletions(-) create mode 100644 doc/content/physics/elasticity.md delete mode 100644 doc/content/physics/index.md delete mode 100644 doc/content/physics/solid_mechanics.md delete mode 100644 doc/content/system/index.md diff --git a/doc/config/Doxyfile.in b/doc/config/Doxyfile.in index 0532308cc0..4ea38128df 100644 --- a/doc/config/Doxyfile.in +++ b/doc/config/Doxyfile.in @@ -7,7 +7,7 @@ DOXYFILE_ENCODING = UTF-8 PROJECT_NAME = NEML2 PROJECT_NUMBER = 1.4.0 OUTPUT_DIRECTORY = build -TOC_INCLUDE_HEADINGS = 2 +TOC_INCLUDE_HEADINGS = 3 #--------------------------------------------------------------------------- # Build related configuration options diff --git a/doc/config/DoxygenLayout.xml b/doc/config/DoxygenLayout.xml index a1113003ce..af4a93ea5d 100644 --- a/doc/config/DoxygenLayout.xml +++ b/doc/config/DoxygenLayout.xml @@ -11,14 +11,14 @@ - + - + diff --git a/doc/config/HTML.in b/doc/config/HTML.in index 333c0c2575..f248300aeb 100644 --- a/doc/config/HTML.in +++ b/doc/config/HTML.in @@ -15,4 +15,4 @@ USE_MATHJAX = YES MATHJAX_VERSION = MathJax_3 MATHJAX_FORMAT = HTML-CSS MATHJAX_RELPATH = https://cdn.jsdelivr.net/npm/mathjax@3 -MATHJAX_EXTENSIONS = ams physics +MATHJAX_EXTENSIONS = ams physics boldsymbol diff --git a/doc/content/physics/elasticity.md b/doc/content/physics/elasticity.md new file mode 100644 index 0000000000..af93b72dfd --- /dev/null +++ b/doc/content/physics/elasticity.md @@ -0,0 +1,414 @@ +# Solid Mechanics {#solid-mechanics} + +[TOC] + +The solid mechanics physics module is a collection objects serving as building blocks for composing material models for solids. Each category of the material model is explained below, with both the mathematical formulations and example input files. + +## Elasticity + +Elasticity models describe the relationship between stress \f$ \boldsymbol{\sigma} \f$ and strain \f$ \boldsymbol{\varepsilon} \f$ without any history-dependent (internal) state variables. In general, the stress-strain relation can be written as + +\f[ + \boldsymbol{\sigma} = \mathbb{C} : \boldsymbol{\varepsilon} +\f] + +where \f$ \mathbb{C} \f$ is the fourth-order elasticity tensor. For linear isotropic elasticity, this relation can be simplified as + +\f[ + \boldsymbol{\sigma} = 3 K \operatorname{vol} \boldsymbol{\varepsilon} + 2 G \operatorname{dev} \boldsymbol{\varepsilon} +\f] + +where \f$ K \f$ is the bulk modulus, and \f$ G \f$ is the shear modulus. + +Below is an example input file that defines a linear elasticity model. + +```python +[Models] + [model] + type = LinearIsotropicElasticity + youngs_modulus = 100 + poisson_ratio = 0.3 + [] +[] +``` + +## Plasticity (macroscale) + +Generally speaking, plasticity models describe (oftentimes irreversible and dissipative) history-dependent deformation of solid materials. The plastic deformation is governed by the plastic loading/unloading conditions (or more generally the Karush-Kuhn-Tucker conditions): + +\f{align*} + f^p \leq 0, \quad \dot{\gamma} \geq 0, \quad \dot{\gamma}f^p = 0, \\ +\f} + +where \f$ f^p \f$ is the yield function, and \f$ \gamma \f$ is the consistency parameter. + +### Consistent plasticity + +Consistent plasticity refers to the family of (macroscale) plasticity models that solves the plastic loading/unloading conditions (or the KKT conditions) exactly (up to machine precision). + +> Consistent plasticity models are sometimes considered rate-independent. But that is a misnomer as rate sensitivity can be baked into the yield function definition in terms of the rates of the internal variables. + +Residual associated with the KKT conditions can be written as the complementarity condition + +\f{align*} + r = + \begin{cases} + \dot{\gamma}, & f^p \leq 0 \\ + f^p, & f^p \geq 0. + \end{cases} +\f} + +This complementarity condition is implemented by the object `RateIndependentPlasticFlowConstraint`. A complete example input file for consistent plasticity is shown below, and the composition and possible modifications are explained in the following subsections. + +``` +[Models] + [elastic_strain] + type = ElasticStrain + [] + [elasticity] + type = LinearIsotropicElasticity + youngs_modulus = 1e5 + poisson_ratio = 0.3 + [] + [vonmises] + type = SR2Invariant + invariant_type = 'VONMISES' + tensor = 'state/internal/S' + invariant = 'state/internal/s' + [] + [yield_function] + type = YieldFunction + yield_stress = 1000 + [] + [flow] + type = ComposedModel + models = 'vonmises yield_function' + [] + [normality] + type = Normality + model = 'flow' + function = 'state/internal/fp' + from = 'state/internal/S' + to = 'state/internal/NM' + [] + [Eprate] + type = AssociativePlasticFlow + [] + [integrate_Ep] + type = SR2BackwardEulerTimeIntegration + variable = 'internal/Ep' + [] + [consistency] + type = RateIndependentPlasticFlowConstraint + [] + [surface] + type = ComposedModel + models = "elastic_strain elasticity + vonmises yield_function normality Eprate + consistency integrate_Ep" + [] + [return_map] + type = ImplicitUpdate + implicit_model = 'surface' + solver = 'newton' + [] + [model] + type = ComposedModel + models = 'return_map elastic_strain elasticity' + additional_outputs = 'state/internal/Ep' + [] +[] +``` + +### Viscoplasticity + +Viscoplasticity models regularize the KKT conditions by introducing approximations to the constraints. A widely adopted approximation is the Perzyna model where rate sensitivity is baked into the approximation following a power-law relation: + +\f{align*} + \dot{\gamma} = \left( \dfrac{\left< f^p \right>}{\eta} \right)^n, +\f} + +where \f$ \eta \f$ is the reference stress and \f$ n \f$ is the power-law exponent. + +The Perzyna model is implemented by the object `PerzynaPlasticFlowRate`. A complete example input file for viscoplasticity is shown below, and the composition and possible modifications are explained in the following subsections. + +``` +[Models] + [elastic_strain] + type = SR2SumModel + from_var = 'forces/E state/internal/Ep' + to_var = 'state/internal/Ee' + coefficients = '1 -1' + [] + [elasticity] + type = LinearIsotropicElasticity + youngs_modulus = 1e5 + poisson_ratio = 0.3 + [] + [vonmises] + type = SR2Invariant + invariant_type = 'VONMISES' + tensor = 'state/internal/S' + invariant = 'state/internal/s' + [] + [yield_function] + type = YieldFunction + yield_stress = 5 + [] + [flow] + type = ComposedModel + models = 'vonmises yield_function' + [] + [normality] + type = Normality + model = 'flow' + function = 'state/internal/fp' + from = 'state/internal/S' + to = 'state/internal/NM' + [] + [flow_rate] + type = PerzynaPlasticFlowRate + reference_stress = 100 + exponent = 2 + [] + [Eprate] + type = AssociativePlasticFlow + [] + [integrate_Ep] + type = SR2BackwardEulerTimeIntegration + variable = 'internal/Ep' + [] + [implicit_rate] + type = ComposedModel + models = "isoharden elastic_strain elasticity + vonmises yield_function flow_rate + normality Eprate integrate_Ep" + [] + [return_map] + type = ImplicitUpdate + implicit_model = 'implicit_rate' + solver = 'newton' + [] + [model] + type = ComposedModel + models = 'return_map elastic_strain elasticity' + additional_outputs = 'state/internal/Ep' + [] +[] +``` + +### Effective stress + +The effective stress is a measure of stress describing how the plastic deformation "flows". For example, the widely-used \f$ J_2 \f$ plasticity uses the von Mises stress as the stress measure, i.e., + +\f{align*} + \bar{\sigma} &= \sqrt{3 J_2}, \\ + J_2 &= \frac{1}{2} \operatorname{dev} \boldsymbol{\sigma} : \operatorname{dev} \boldsymbol{\sigma}. +\f} + +Commonly used stress measures are defined using `SR2Invariant`. + +```python +[Models] + [vonmises] + type = SR2Invariant + invariant_type = 'VONMISES' + tensor = 'state/internal/S' + invariant = 'state/internal/s' + [] +[] +``` + +### Perfectly Plastic Yield function + +For perfectly plastic materials, the yield function only depends on the effective stress and a constant yield stress, i.e., the envelope does not shrink or expand depending on the loading history. + +\f{align*} + f^p &= \bar{\sigma} - \sigma_y. +\f} + +Below is an example input file defining a perfectly plastic yield function with \f$ J_2 \f$ flow. + +```python +[Models] + [vonmises] + type = SR2Invariant + invariant_type = 'VONMISES' + tensor = 'state/internal/S' + invariant = 'state/internal/s' + [] + [yield_function] + type = YieldFunction + yield_stress = 5 + [] +[] +``` + +### Isotropic hardening + +The equivalent plastic strain \f$ \bar{\varepsilon}^p \f$ is a scalar-valued internal variable that can be introduced to control the shape of the yield function. The isotropic strain hardening \f$ k \f$ is controlled by the accumulated equivalent plastic strain, and enters the yield function as + +\f{align*} + f^p &= \bar{\sigma} - \sigma_y - k(\bar{\varepsilon}^p). +\f} + +Below is an example input file defining a yield function with \f$ J_2 \f$ flow and linear isotropic hardening. + +```python +[Models] + [vonmises] + type = SR2Invariant + invariant_type = 'VONMISES' + tensor = 'state/internal/S' + invariant = 'state/internal/s' + [] + [isoharden] + type = LinearIsotropicHardening + hardening_modulus = 1000 + [] + [yield_function] + type = YieldFunction + yield_stress = 5 + isotropic_hardening = 'state/internal/k' + [] +[] +``` + +### Kinematic hardening + +The kinematic plastic strain \f$ \boldsymbol{K}^p \f$ is a tensor-valued internal variable that can be introduced to control the shape of the yield function. The kinematic hardening \f$ X \f$ is controlled by the accumulated kinematic plastic strain, and the effective stress is defined in terms of the over stress. In case of \f$ J_2 \f$, the effective stress can be rewritten as + +\f{align*} + \bar{\sigma} &= \sqrt{3 J_2}, \\ + J_2 &= \frac{1}{2} \operatorname{dev} \boldsymbol{\Xi} : \operatorname{dev} \boldsymbol{\Xi}, \\ + \boldsymbol{\Xi} &= \boldsymbol{\sigma} - \boldsymbol{X}. +\f} + +Below is an example input file defining a yield function with \f$ J_2 \f$ flow and linear kinematic hardening. + +```python +[Models] + [kinharden] + type = LinearKinematicHardening + hardening_modulus = 1000 + [] + [overstress] + type = SR2SumModel + from_var = 'state/internal/S state/internal/X' + to_var = 'state/internal/O' + coefficients = '1 -1' + [] + [vonmises] + type = SR2Invariant + invariant_type = 'VONMISES' + tensor = 'state/internal/O' + invariant = 'state/internal/s' + [] + [yield_function] + type = YieldFunction + yield_stress = 5 + [] +[] +``` + +### Back stress + +An alternative way of introducing hardening is through back stresses. Instead of modeling the accumulation of kinematic plastic strain, back stress models directly describe the evolution of back stress. An example input file defining a yield function with \f$ J_2 \f$ flow and two back stresses is shown below. + +```python +[Models] + [overstress] + type = SR2SumModel + from_var = 'state/internal/S state/internal/X1 state/internal/X2' + to_var = 'state/internal/O' + coefficients = '1 -1 -1' + [] + [vonmises] + type = SR2Invariant + invariant_type = 'VONMISES' + tensor = 'state/internal/O' + invariant = 'state/internal/s' + [] + [yield_function] + type = YieldFunction + yield_stress = 5 + [] +[] +``` + +### Mixed hardening + +Isotropic hardening, kinematic hardening, and back stresses are all optional and can be "mixed" in the definition of a yield function. The example input file below shows a yield function with \f$ J_2 \f$ flow, isotropic hardening, kinematic hardening, and two back stresses. + +```python +[Models] + [isoharden] + type = LinearIsotropicHardening + hardening_modulus = 1000 + [] + [kinharden] + type = LinearKinematicHardening + hardening_modulus = 1000 + back_stress = 'state/internal/X0' + [] + [overstress] + type = SR2SumModel + from_var = 'state/internal/S state/internal/X0 state/internal/X1 state/internal/X2' + to_var = 'state/internal/O' + coefficients = '1 -1 -1 -1' + [] + [vonmises] + type = SR2Invariant + invariant_type = 'VONMISES' + tensor = 'state/internal/O' + invariant = 'state/internal/s' + [] + [yield_function] + type = YieldFunction + yield_stress = 5 + isotropic_hardening = 'state/internal/k' + [] +[] +``` + +### Flow rules + +Flow rules are required to map from the consistency parameter \f$ \gamma \f$ to various internal variables describing the state of hardening, such as the equivalent plastic strain \f$ \bar{\varepsilon}^p \f$, the kinematic plastic strain \f$ \boldsymbol{K}^p \f$, and the back stress \f$ \boldsymbol{X} \f$. + +Associative flow rules define flow directions variationally according to the principle of maximum dissipation, i.e., + +\f{align*} + \dot{\boldsymbol{\varepsilon}}^p &= \dot{\gamma} \dfrac{\partial f^p}{\partial \boldsymbol{\sigma}}, \\ + \dot{\bar{\varepsilon}}^p &= -\dot{\gamma} \dfrac{\partial f^p}{\partial k}, \\ + \dot{\boldsymbol{K}}^p &= \dot{\gamma} \dfrac{\partial f^p}{\partial \boldsymbol{X}}. +\f} + +The example input file below defines associative \f$ J_2 \f$ flow rules + +```python + [flow] + ... + [] + [normality] + type = Normality + model = 'flow' + function = 'state/internal/fp' + from = 'state/internal/S state/internal/k state/internal/X' + to = 'state/internal/NM state/internal/Nk state/internal/NX' + [] + [eprate] + type = AssociativeIsotropicPlasticHardening + [] + [Kprate] + type = AssociativeKinematicPlasticHardening + [] + [Eprate] + type = AssociativePlasticFlow + [] +[] +``` + +In the above example, a model named "normality" is used to compute the associative flow directions, and the rates of the internal variables are mapped using the rate of the consistency parameter and each of the associative flow direction. The cross-referenced model named "flow" (omitted in the example) is the composition of models defining the yield function \f$ f^p \f$ in terms of the variational arguments \f$ \boldsymbol{\sigma} \f$, \f$ k \f$, and \f$ \boldsymbol{X} \f$. + + +## Crystal plasticity diff --git a/doc/content/physics/index.md b/doc/content/physics/index.md deleted file mode 100644 index 182bd93a4c..0000000000 --- a/doc/content/physics/index.md +++ /dev/null @@ -1,5 +0,0 @@ -# Physics Documentation {#physics} - -| Physics module | Description | -| :-------------------------------------- | :-------------------------------------------------------- | -| [Solid mechanics](@ref solid-mechanics) | Elastic, plastic, and viscous behavior of solid materials | diff --git a/doc/content/physics/solid_mechanics.md b/doc/content/physics/solid_mechanics.md deleted file mode 100644 index 75e3274a5e..0000000000 --- a/doc/content/physics/solid_mechanics.md +++ /dev/null @@ -1 +0,0 @@ -# Solid Mechanics {#solid-mechanics} diff --git a/doc/content/system/index.md b/doc/content/system/index.md deleted file mode 100644 index e1476a814a..0000000000 --- a/doc/content/system/index.md +++ /dev/null @@ -1,10 +0,0 @@ -# System Documentation {#system} - -| System | Description | -| :---------------------------- | :------------------------------------------ | -| [Tensor](@ref system-tensors) | Tensors created at run-time | -| [Model](@ref system-models) | The model or the composition of models | -| [Solver](@ref system-solvers) | Solvers used to solve a system of equations | -| [Data](@ref system-data) | Data used by models | -| [Driver](@ref system-drivers) | Predefined drivers to run models | - From 97dd57c32ea2b9df1db57f39a691e4f01fa1ff69 Mon Sep 17 00:00:00 2001 From: Gary Hu Date: Tue, 14 May 2024 15:13:26 -0500 Subject: [PATCH 25/55] Install graphviz; Fix all doxygen warnings --- .github/workflows/build_docs.yml | 1 + doc/content/getting_started.md | 2 +- doc/content/system/tensor.md | 2 +- include/neml2/misc/math.h | 2 +- include/neml2/models/BufferStore.h | 4 ++-- include/neml2/models/Model.h | 2 +- include/neml2/models/ParameterStore.h | 4 ++-- .../crystal_plasticity/FixOrientation.h | 2 +- include/neml2/solvers/NonlinearSystem.h | 2 +- include/neml2/tensors/LabeledAxis.h | 2 +- include/neml2/tensors/user_tensors/Orientation.h | 8 -------- scripts/syntax_to_md.py | 1 + src/neml2/tensors/user_tensors/Orientation.cxx | 15 +++++++++++++++ 13 files changed, 28 insertions(+), 19 deletions(-) diff --git a/.github/workflows/build_docs.yml b/.github/workflows/build_docs.yml index 1e4993b059..01bc890769 100644 --- a/.github/workflows/build_docs.yml +++ b/.github/workflows/build_docs.yml @@ -29,6 +29,7 @@ jobs: - uses: actions/setup-python@v5 with: python-version: "3.8" + - run: sudo apt install graphviz - run: pip install -r requirements.txt - run: | cmake \ diff --git a/doc/content/getting_started.md b/doc/content/getting_started.md index 8b53f1921c..2130bf028f 100644 --- a/doc/content/getting_started.md +++ b/doc/content/getting_started.md @@ -41,7 +41,7 @@ All NEML2 capabilities that can be defined through the input file fall under a n [] [] ``` -defines a tensor named "E" under the `[Tensors]` block and a model named "elasticity" under the `[Models]` block. The [Syntax Documentation](@ref syntax) provides a complete list of objects that can be defined by an input file. The [System Documentation](@ref system) provides detailed explanation of each system. +defines a tensor named "E" under the `[Tensors]` block and a model named "elasticity" under the `[Models]` block. The [Syntax Documentation](@ref syntax-tensors) provides a complete list of objects that can be defined by an input file. The [System Documentation](@ref system-tensors) provides detailed explanation of each system. ## Special syntax diff --git a/doc/content/system/tensor.md b/doc/content/system/tensor.md index ff716e410f..c8dfd835f8 100644 --- a/doc/content/system/tensor.md +++ b/doc/content/system/tensor.md @@ -47,7 +47,7 @@ neml2_assert(batch_sz == {5, 2}); | [R5](@ref neml2::R5) | \f$(3,3,3,3,3)\f$ | Rank-5 tensor | | [SSFR5](@ref neml2::SSFR5) | \f$(6,6,3)\f$ | Rank-5 tensor with minor symmetry on base dimensions 0-3 | | [Rot](@ref neml2::Rot) | \f$(3)\f$ | Rotation tensor represented in the Rodrigues form | -| [Quarternion](@ref neml2::Quarternion) | \f$(4)\f$ | Quarternion | +| [Quaternion](@ref neml2::Quaternion) | \f$(4)\f$ | Quaternion | | [MillerIndex](@ref neml2::MillerIndex) | \f$(3)\f$ | Crystal direction or lattice plane represented as Miller indices | Furthermore, all primitive tensor types can be "registered" as variables on a `LabeledAxis`, which will be discussed in the following section on [labeled view](@ref tensor-labeling). diff --git a/include/neml2/misc/math.h b/include/neml2/misc/math.h index 267f7431b9..9eab1f31b5 100644 --- a/include/neml2/misc/math.h +++ b/include/neml2/misc/math.h @@ -207,7 +207,7 @@ BatchTensor skew_to_full(const BatchTensor & skew, TorchSize dim = 0); * @param p The parameter to take derivatives with respect to * @return BatchTensor \f$\partial y/\partial p\f$ */ -BatchTensor jacrev(const BatchTensor & out, const BatchTensor & p); +BatchTensor jacrev(const BatchTensor & y, const BatchTensor & p); BatchTensor base_diag_embed(const BatchTensor & a, TorchSize offset = 0, TorchSize d1 = -2, TorchSize d2 = -1); diff --git a/include/neml2/models/BufferStore.h b/include/neml2/models/BufferStore.h index 5228b6d73a..22518181ba 100644 --- a/include/neml2/models/BufferStore.h +++ b/include/neml2/models/BufferStore.h @@ -67,7 +67,7 @@ class BufferStore * allowed, but only the first call to declare_buffer constructs the buffer value, and subsequent * calls only returns a reference to the existing buffer. * - * @tparam T Buffer type. See @ref primitive for supported types. + * @tparam T Buffer type. See @ref statically-shaped-tensor for supported types. * @param name Buffer name * @param rawval Buffer value * @return Reference to buffer @@ -84,7 +84,7 @@ class BufferStore * allowed, but only the first call to declare_buffer constructs the buffer value, and subsequent * calls only returns a reference to the existing buffer. * - * @tparam T Buffer type. See @ref primitive for supported types. + * @tparam T Buffer type. See @ref statically-shaped-tensor for supported types. * @param name Buffer name * @param input_option_name Name of the input option that defines the value of the model * buffer. diff --git a/include/neml2/models/Model.h b/include/neml2/models/Model.h index 69e98347d5..b3527b1771 100644 --- a/include/neml2/models/Model.h +++ b/include/neml2/models/Model.h @@ -242,7 +242,7 @@ class Model : public std::enable_shared_from_this, * If \p merge_input is set to true, this model will also *consume* the consumed variables of \p * model, which will affect dependency resolution inside a ComposedModel. * - * @param model The model to register + * @param name The model to register * @param extra_deriv_order The additional derivative order required for the registered-submodel * @param nonlinear Set to true if the registered model defines a nonlinear system to be solved * @param merge_input Whether to merge the input of the registered model into *this* model's diff --git a/include/neml2/models/ParameterStore.h b/include/neml2/models/ParameterStore.h index 2d0b2ba928..93579243e7 100644 --- a/include/neml2/models/ParameterStore.h +++ b/include/neml2/models/ParameterStore.h @@ -83,7 +83,7 @@ class ParameterStore * allowed, but only the first call to declare_parameter constructs the parameter value, and * subsequent calls only returns a reference to the existing parameter. * - * @tparam T Buffer type. See @ref primitive for supported types. + * @tparam T Buffer type. See @ref statically-shaped-tensor for supported types. * @param name Buffer name * @param rawval Buffer value * @return Reference to buffer @@ -100,7 +100,7 @@ class ParameterStore * allowed, but only the first call to declare_parameter constructs the parameter value, and * subsequent calls only returns a reference to the existing parameter. * - * @tparam T Parameter type. See @ref primitive for supported types. + * @tparam T Parameter type. See @ref statically-shaped-tensor for supported types. * @param name Name of the model parameter. * @param input_option_name Name of the input option that defines the value of the model * parameter. diff --git a/include/neml2/models/solid_mechanics/crystal_plasticity/FixOrientation.h b/include/neml2/models/solid_mechanics/crystal_plasticity/FixOrientation.h index 5e9aeb4756..0f3713581f 100644 --- a/include/neml2/models/solid_mechanics/crystal_plasticity/FixOrientation.h +++ b/include/neml2/models/solid_mechanics/crystal_plasticity/FixOrientation.h @@ -29,7 +29,7 @@ namespace neml2 { /** - * @brief Swap orientation plane when the singularity at \f[2 \pi\] is met with the modified + * @brief Swap orientation plane when the singularity at \f$ 2 \pi \f$ is met with the modified * Rodrigues vector. * * See the following reference for details diff --git a/include/neml2/solvers/NonlinearSystem.h b/include/neml2/solvers/NonlinearSystem.h index aa61673570..81ff2730a1 100644 --- a/include/neml2/solvers/NonlinearSystem.h +++ b/include/neml2/solvers/NonlinearSystem.h @@ -93,7 +93,7 @@ class NonlinearSystem * @param residual Whether residual is requested * @param Jacobian Whether Jacobian is requested */ - virtual void assemble(bool, bool) = 0; + virtual void assemble(bool residual, bool Jacobian) = 0; /// Number of degrees of freedom TorchSize _ndof; diff --git a/include/neml2/tensors/LabeledAxis.h b/include/neml2/tensors/LabeledAxis.h index 71c73e25f6..926405fbce 100644 --- a/include/neml2/tensors/LabeledAxis.h +++ b/include/neml2/tensors/LabeledAxis.h @@ -39,7 +39,7 @@ namespace neml2 * @brief A *labeled* axis used to associate layout of a tensor with human-interpretable names. * * A logically one-dimensional tensor requires one LabeledAxis, two-dimensional tensor requires two - * LabeledAxis, and so on. See @ref labeledview for a detailed explanation of tensor labeling. + * LabeledAxis, and so on. See @ref tensor-labeling for a detailed explanation of tensor labeling. * * All the LabeledAxis modifiers can only be used during the setup stage. Calling any modifiers * after the setup stage is forbidden and will result in a runtime error in Debug mode. diff --git a/include/neml2/tensors/user_tensors/Orientation.h b/include/neml2/tensors/user_tensors/Orientation.h index 28359125f3..5c053da6a0 100644 --- a/include/neml2/tensors/user_tensors/Orientation.h +++ b/include/neml2/tensors/user_tensors/Orientation.h @@ -41,14 +41,6 @@ class Orientation : public Rot, public UserTensor /** * @brief Construct a new Orientation object * - * @param "input_type" -- the method used to define the angles, "euler_angles" or "random" - * @param "angle_convention" -- Euler angle convention, "Kocks", "Roe", or "Bunge" - * @param "angle_type" -- type of angles, either "degrees" or "radians" - * @param "values" -- input Euler angles, as a flattened nx3 matrix - * @param "normalize" -- if true do a "shadow parameter" replacement of the underlying MRP - * representation to move the inputs farther away from the singularity - * @param "random_seed" -- random seed for random angle generation - * @param "quantity" -- number (batch size) of random orientations */ Orientation(const OptionSet & options); diff --git a/scripts/syntax_to_md.py b/scripts/syntax_to_md.py index 9701ce79d3..8f67ae82a2 100755 --- a/scripts/syntax_to_md.py +++ b/scripts/syntax_to_md.py @@ -70,6 +70,7 @@ def get_sections(syntax): "# [{}] {{#{}}}\n\n".format(section, "syntax-" + section.lower()) ) stream.write("[TOC]\n\n") + stream.write("## Available objects and their input file syntax\n\n") stream.write( "Refer to [Systam Documentation](@ref system-{}) for detailed explanation about this system.\n\n".format( section.lower() diff --git a/src/neml2/tensors/user_tensors/Orientation.cxx b/src/neml2/tensors/user_tensors/Orientation.cxx index 24d14cef18..cbe423fcc2 100644 --- a/src/neml2/tensors/user_tensors/Orientation.cxx +++ b/src/neml2/tensors/user_tensors/Orientation.cxx @@ -37,14 +37,29 @@ Orientation::expected_options() { OptionSet options = UserTensor::expected_options(); options.set("input_type") = "euler_angles"; + options.set("input_type").doc() = + "The method used to define the angles, 'euler_angles' or 'random'"; + options.set("angle_convention") = "kocks"; + options.set("angle_convention").doc() = "Euler angle convention, 'Kocks', 'Roe', or 'Bunge'"; + options.set("angle_type") = "degrees"; + options.set("angle_type").doc() = "Type of angles, either 'degrees' or 'radians'"; + options.set>("values") = {}; + options.set("values").doc() = "Input Euler angles, as a flattened n-by-3 matrix"; + options.set("normalize") = false; + options.set("normalize").doc() = + "If true do a shadow parameter replacement of the underlying MRP representation to move the " + "inputs farther away from the singularity"; options.set("random_seed") = -1; + options.set("random_seed").doc() = "Random seed for random angle generation"; options.set("quantity") = 1; + options.set("quantity").doc() = "Number (batch size) of random orientations"; + return options; } From 7e7d32395b9b25f3b9b20e8cc834b1489babcda8 Mon Sep 17 00:00:00 2001 From: Gary Hu Date: Wed, 15 May 2024 15:36:17 -0500 Subject: [PATCH 26/55] Fixing typos and stubgen --- cmake/Modules/FindTorch.cmake | 1 - doc/CMakeLists.txt | 4 +- doc/config/Doxyfile.in | 2 + doc/config/HTML.in | 2 - doc/config/header.html | 93 ------------------- .../{elasticity.md => solid_mechanics.md} | 4 +- python/CMakeLists.txt | 1 - python/neml2/base/Factory.cxx | 5 +- python/neml2/models/Model.cxx | 7 ++ python/neml2/tensors/TensorValue.cxx | 5 +- scripts/syntax_to_md.py | 2 +- src/neml2/CMakeLists.txt | 1 - tests/CMakeLists.txt | 2 - 13 files changed, 16 insertions(+), 113 deletions(-) delete mode 100644 doc/config/header.html rename doc/content/physics/{elasticity.md => solid_mechanics.md} (98%) diff --git a/cmake/Modules/FindTorch.cmake b/cmake/Modules/FindTorch.cmake index c14999840f..d8741f502e 100644 --- a/cmake/Modules/FindTorch.cmake +++ b/cmake/Modules/FindTorch.cmake @@ -38,7 +38,6 @@ if(NOT DEFINED LIBTORCH_DIR) endif() endif() - message(STATUS "Downloading libTorch, this may take a few minutes.") FetchContent_MakeAvailable(torch) if(NOT torch_SOURCE_DIR) diff --git a/doc/CMakeLists.txt b/doc/CMakeLists.txt index a77031e1bf..84a6a78d38 100644 --- a/doc/CMakeLists.txt +++ b/doc/CMakeLists.txt @@ -4,7 +4,6 @@ if(NOT DEFINED DOXYGEN_EXECUTABLE) doxygen URL https://github.com/doxygen/doxygen/releases/download/Release_1_10_0/doxygen-1.10.0.linux.bin.tar.gz ) - message(STATUS "Downloading doxygen, this may take a few minutes.") FetchContent_MakeAvailable(doxygen) set(DOXYGEN_EXECUTABLE ${doxygen_SOURCE_DIR}/bin/doxygen) else() @@ -18,7 +17,6 @@ FetchContent_Declare( GIT_REPOSITORY https://github.com/jothepro/doxygen-awesome-css.git GIT_TAG v2.3.2 ) -message(STATUS "Downloading doxygen-awesome-css, this may take a few minutes.") FetchContent_MakeAvailable(doxygen-awesome-css) find_package(Python COMPONENTS Interpreter) @@ -59,7 +57,7 @@ add_custom_target(doc-syntax VERBATIM ) -add_custom_target(doc-html +add_custom_target(doc-html ALL DEPENDS doc-syntax WORKING_DIRECTORY ${NEML2_BINARY_DIR}/doc COMMAND ${DOXYGEN_EXECUTABLE} -q DoxyfileHTML.sh diff --git a/doc/config/Doxyfile.in b/doc/config/Doxyfile.in index 4ea38128df..c73bb9bc3e 100644 --- a/doc/config/Doxyfile.in +++ b/doc/config/Doxyfile.in @@ -36,7 +36,9 @@ INPUT = ${NEML2_SOURCE_DIR}/README.md \ ${NEML2_BINARY_DIR}/doc/content FILE_PATTERNS = *.cxx \ *.h \ + *.pyi \ *.md +EXTENSION_MAPPING = pyi=Python RECURSIVE = YES IMAGE_PATH = ${NEML2_SOURCE_DIR}/doc/content/asset EXCLUDE_SYMBOLS = neml2::*internal neml2::*details diff --git a/doc/config/HTML.in b/doc/config/HTML.in index f248300aeb..0d9ace4574 100644 --- a/doc/config/HTML.in +++ b/doc/config/HTML.in @@ -5,10 +5,8 @@ GENERATE_HTML = YES DISABLE_INDEX = NO FULL_SIDEBAR = NO HTML_OUTPUT = html -HTML_HEADER = ${NEML2_SOURCE_DIR}/doc/config/header.html HTML_EXTRA_STYLESHEET = ${doxygen-awesome-css_SOURCE_DIR}/doxygen-awesome.css\ ${doxygen-awesome-css_SOURCE_DIR}/doxygen-awesome-sidebar-only.css -HTML_EXTRA_FILES = ${doxygen-awesome-css_SOURCE_DIR}/doxygen-awesome-paragraph-link.js HTML_COLORSTYLE = LIGHT GENERATE_TREEVIEW = YES USE_MATHJAX = YES diff --git a/doc/config/header.html b/doc/config/header.html deleted file mode 100644 index 60cd7b045d..0000000000 --- a/doc/config/header.html +++ /dev/null @@ -1,93 +0,0 @@ - - - - - - - - - - - $projectname: $title - - $title - - - - - - - - - - - - - - - $treeview - $search - $mathjax - $darkmode - - $extrastylesheet - - - - - - - -
- - - -
- - -
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
-
$projectname $projectnumber -
- -
$projectbrief
-
-
$projectbrief
-
$searchbox
$searchbox
-
- - diff --git a/doc/content/physics/elasticity.md b/doc/content/physics/solid_mechanics.md similarity index 98% rename from doc/content/physics/elasticity.md rename to doc/content/physics/solid_mechanics.md index af93b72dfd..7642b3601a 100644 --- a/doc/content/physics/elasticity.md +++ b/doc/content/physics/solid_mechanics.md @@ -44,7 +44,7 @@ where \f$ f^p \f$ is the yield function, and \f$ \gamma \f$ is the consistency p ### Consistent plasticity -Consistent plasticity refers to the family of (macroscale) plasticity models that solves the plastic loading/unloading conditions (or the KKT conditions) exactly (up to machine precision). +Consistent plasticity refers to the family of (macroscale) plasticity models that solve the plastic loading/unloading conditions (or the KKT conditions) exactly (up to machine precision). > Consistent plasticity models are sometimes considered rate-independent. But that is a misnomer as rate sensitivity can be baked into the yield function definition in terms of the rates of the internal variables. @@ -53,7 +53,7 @@ Residual associated with the KKT conditions can be written as the complementarit \f{align*} r = \begin{cases} - \dot{\gamma}, & f^p \leq 0 \\ + \dot{\gamma}, & f^p < 0 \\ f^p, & f^p \geq 0. \end{cases} \f} diff --git a/python/CMakeLists.txt b/python/CMakeLists.txt index f11e1dd8d2..595b1af1cd 100644 --- a/python/CMakeLists.txt +++ b/python/CMakeLists.txt @@ -4,7 +4,6 @@ FetchContent_Declare( GIT_REPOSITORY https://github.com/pybind/pybind11.git GIT_TAG v2.12.0 ) -message(STATUS "Downloading pybind11, this may take a few minutes.") FetchContent_MakeAvailable(pybind11) if(NOT Torch_PYTHON_BINDING) diff --git a/python/neml2/base/Factory.cxx b/python/neml2/base/Factory.cxx index c7951022ae..af13af0987 100644 --- a/python/neml2/base/Factory.cxx +++ b/python/neml2/base/Factory.cxx @@ -35,8 +35,5 @@ def_Factory(py::module_ & m) { py::class_(m, "Factory") .def_static("load", &Factory::load) - .def_static("clear", &Factory::clear) - .def_static("get_model", - [](const std::string & name) - { return Factory::get_object_ptr("Models", name); }); + .def_static("clear", &Factory::clear); } diff --git a/python/neml2/models/Model.cxx b/python/neml2/models/Model.cxx index 3d99db18ce..7d30fb0f06 100644 --- a/python/neml2/models/Model.cxx +++ b/python/neml2/models/Model.cxx @@ -26,6 +26,7 @@ #include "python/neml2/misc/types.h" #include "neml2/models/Model.h" +#include "neml2/base/Factory.h" namespace py = pybind11; using namespace neml2; @@ -55,4 +56,10 @@ def_Model(py::module_ & m) "named_parameters", [](Model & self) { return &self.named_parameters(); }, py::return_value_policy::reference); + + // Make Model manufacturable + auto factory = (py::class_)py::module_::import("neml2.base").attr("Factory"); + factory.def_static("get_model", + [](const std::string & name) + { return Factory::get_object_ptr("Models", name); }); } diff --git a/python/neml2/tensors/TensorValue.cxx b/python/neml2/tensors/TensorValue.cxx index be48a53049..b4ef4c3f4f 100644 --- a/python/neml2/tensors/TensorValue.cxx +++ b/python/neml2/tensors/TensorValue.cxx @@ -42,8 +42,8 @@ def_TensorValueBase(py::module_ & m) .def("dim", [](const TensorValueBase & self) { return BatchTensor(self).dim(); }) .def_property_readonly("shape", [](const TensorValueBase & self) { return BatchTensor(self).sizes(); }) - .def_property_readonly("dtype", - [](const TensorValueBase & self) { return BatchTensor(self).dtype(); }) + .def_property_readonly( + "dtype", [](const TensorValueBase & self) { return BatchTensor(self).scalar_type(); }) .def_property_readonly( "device", [](const TensorValueBase & self) { return BatchTensor(self).device(); }) .def_property_readonly("requires_grad", @@ -54,5 +54,4 @@ def_TensorValueBase(py::module_ & m) { return BatchTensor(self).requires_grad_(req); }) .def_property_readonly("grad", [](const TensorValueBase & self) { return BatchTensor(self).grad(); }); - ; } diff --git a/scripts/syntax_to_md.py b/scripts/syntax_to_md.py index 8f67ae82a2..59443f66c4 100755 --- a/scripts/syntax_to_md.py +++ b/scripts/syntax_to_md.py @@ -72,7 +72,7 @@ def get_sections(syntax): stream.write("[TOC]\n\n") stream.write("## Available objects and their input file syntax\n\n") stream.write( - "Refer to [Systam Documentation](@ref system-{}) for detailed explanation about this system.\n\n".format( + "Refer to [System Documentation](@ref system-{}) for detailed explanation about this system.\n\n".format( section.lower() ) ) diff --git a/src/neml2/CMakeLists.txt b/src/neml2/CMakeLists.txt index b791edf23a..975b86f885 100644 --- a/src/neml2/CMakeLists.txt +++ b/src/neml2/CMakeLists.txt @@ -59,7 +59,6 @@ FetchContent_Declare( GIT_TAG 100b575af08643b5e646cac8faff6c87dd1c15a7 SOURCE_DIR ${NEML2_BINARY_DIR}/_deps/hit/hit ) -message(STATUS "Downloading hit, this may take a few minutes.") FetchContent_MakeAvailable(hit) add_library(hit SHARED diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt index 7894cf07f6..8afc275efc 100644 --- a/tests/CMakeLists.txt +++ b/tests/CMakeLists.txt @@ -7,7 +7,6 @@ if(NEML2_UNIT OR NEML2_REGRESSION OR NEML2_VERIFICATION OR NEML2_BENCHMARK) GIT_REPOSITORY https://github.com/catchorg/Catch2.git GIT_TAG v3.5.4 ) - message(STATUS "Downloading Catch2, this may take a few minutes.") FetchContent_MakeAvailable(Catch2) # Catch2 v3 is built as a static library, so we MUST make sure the compile definitions are compatible with ours @@ -179,7 +178,6 @@ if(NEML2_PROFILING) GIT_REPOSITORY https://github.com/gperftools/gperftools.git GIT_TAG gperftools-2.15 ) - message(STATUS "Downloading gperftools, this may take a few minutes.") FetchContent_Populate(gperftools) add_subdirectory(${gperftools_SOURCE_DIR} ${gperftools_BINARY_DIR} EXCLUDE_FROM_ALL) From 3c6c948a40562bb83baa9cd2106050cf02b0bb81 Mon Sep 17 00:00:00 2001 From: Gary Hu Date: Fri, 17 May 2024 15:14:40 -0500 Subject: [PATCH 27/55] Add Python API reference --- .github/workflows/build_docs.yml | 7 +- .github/workflows/python.yml | 2 +- .github/workflows/tests.yml | 6 +- CMakeLists.txt | 126 ++++++-------- cmake-variants.yaml | 8 +- cmake/Modules/FindTorch.cmake | 37 +--- cmake/Modules/NEML2Dependencies.cmake | 57 +++++++ cmake/Modules/NEML2TorchConfig.cmake | 19 +++ cmake/Modules/NEML2UnityGroup.cmake | 13 +- doc/CMakeLists.txt | 131 +++++++------- doc/config/Doxyfile.in | 3 - doc/config/DoxygenLayout.xml | 3 +- doc/config/DoxygenLayoutPython.xml | 228 +++++++++++++++++++++++++ doc/config/HTML.in | 1 + doc/config/{LATEX.in => LaTeX.in} | 1 + doc/config/Python.in | 8 + doc/content/install.md | 4 +- doc/requirements.txt | 1 + python/CMakeLists.txt | 31 ++-- scripts/benchmark.sh | 2 +- src/neml2/CMakeLists.txt | 6 - tests/CMakeLists.txt | 222 +++--------------------- tests/benchmark/CMakeLists.txt | 26 +++ tests/profiler/CMakeLists.txt | 28 +++ tests/{profiling => profiler}/main.cxx | 2 +- tests/{profiling => profiler}/model.i | 0 tests/python/CMakeLists.txt | 9 + tests/regression/CMakeLists.txt | 27 +++ tests/unit/CMakeLists.txt | 27 +++ tests/verification/CMakeLists.txt | 27 +++ 30 files changed, 647 insertions(+), 415 deletions(-) create mode 100644 cmake/Modules/NEML2Dependencies.cmake create mode 100644 doc/config/DoxygenLayoutPython.xml rename doc/config/{LATEX.in => LaTeX.in} (92%) create mode 100644 doc/config/Python.in create mode 100644 tests/benchmark/CMakeLists.txt create mode 100644 tests/profiler/CMakeLists.txt rename tests/{profiling => profiler}/main.cxx (95%) rename tests/{profiling => profiler}/model.i (100%) create mode 100644 tests/python/CMakeLists.txt create mode 100644 tests/regression/CMakeLists.txt create mode 100644 tests/unit/CMakeLists.txt create mode 100644 tests/verification/CMakeLists.txt diff --git a/.github/workflows/build_docs.yml b/.github/workflows/build_docs.yml index 01bc890769..61e6410083 100644 --- a/.github/workflows/build_docs.yml +++ b/.github/workflows/build_docs.yml @@ -39,12 +39,11 @@ jobs: -DNEML2_REGRESSION=OFF \ -DNEML2_VERIFICATION=OFF \ -DNEML2_BENCHMARK=OFF \ - -DNEML2_PROFILING=OFF \ - -DNEML2_PYBIND=OFF \ + -DNEML2_PROFILER=OFF \ + -DNEML2_PYBIND=ON \ -DNEML2_DOC=ON \ . - - run: make doc-syntax -j 2 - - run: make doc-html + - run: make doc-html -j 2 - run: cat doc/doxygen.log - name: Preview GitHub Pages if: ${{ github.event_name == 'pull_request' }} diff --git a/.github/workflows/python.yml b/.github/workflows/python.yml index 304fccd68c..68a1cdc117 100644 --- a/.github/workflows/python.yml +++ b/.github/workflows/python.yml @@ -61,7 +61,7 @@ jobs: -DNEML2_REGRESSION=OFF \ -DNEML2_VERIFICATION=OFF \ -DNEML2_BENCHMARK=OFF \ - -DNEML2_PROFILING=OFF \ + -DNEML2_PROFILER=OFF \ -DNEML2_PYBIND=ON \ -DNEML2_DOC=OFF \ -B build \ diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml index 2069045a6b..4289fe90b4 100644 --- a/.github/workflows/tests.yml +++ b/.github/workflows/tests.yml @@ -72,7 +72,7 @@ jobs: -DNEML2_REGRESSION=ON \ -DNEML2_VERIFICATION=ON \ -DNEML2_BENCHMARK=OFF \ - -DNEML2_PROFILING=OFF \ + -DNEML2_PROFILER=OFF \ -DNEML2_PYBIND=OFF \ -DNEML2_DOC=OFF \ . @@ -121,7 +121,7 @@ jobs: -DNEML2_REGRESSION=ON \ -DNEML2_VERIFICATION=ON \ -DNEML2_BENCHMARK=ON \ - -DNEML2_PROFILING=ON \ + -DNEML2_PROFILER=ON \ -DNEML2_PYBIND=OFF \ -DNEML2_DOC=OFF \ . @@ -151,7 +151,7 @@ jobs: -DNEML2_REGRESSION=ON \ -DNEML2_VERIFICATION=ON \ -DNEML2_BENCHMARK=OFF \ - -DNEML2_PROFILING=OFF \ + -DNEML2_PROFILER=OFF \ -DNEML2_DOC=OFF \ . - run: make -j 2 diff --git a/CMakeLists.txt b/CMakeLists.txt index 6c35109e54..1344761299 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1,94 +1,66 @@ +# ---------------------------------------------------------------------------- +# Project metadata +# ---------------------------------------------------------------------------- cmake_minimum_required(VERSION 3.23) - project(NEML2 VERSION 1.4.0 LANGUAGES CXX) -# Setup modules -set(CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH} "${NEML2_SOURCE_DIR}/cmake/Modules/") - -# Ninja only: limit number of jobs in a CI build -set_property(GLOBAL PROPERTY JOB_POOLS CI=6) - -# FindPython should return the first matching Python -if(POLICY CMP0094) - cmake_policy(SET CMP0094 NEW) -endif() - -# Suppress the warning related to the new policy on fetch content's timestamp -if(POLICY CMP0135) - cmake_policy(SET CMP0135 NEW) -endif() - -# Suppress the warning related to the new policy on FindPythonXXX -if(POLICY CMP0148) - cmake_policy(SET CMP0148 NEW) -endif() - -# Accept Release, Debug, and RelWithDebInfo, add Coverage build types -set(CMAKE_CXX_FLAGS_COVERAGE - "-O0 -fprofile-arcs -ftest-coverage" - CACHE STRING "Flags used by C++ compiler during coverage builds." - FORCE) - -# Set the default build type -if(NOT CMAKE_BUILD_TYPE) - set(CMAKE_BUILD_TYPE "Debug" CACHE - STRING "Choose the type of build." FORCE) - - # Set the possible values of build type for cmake-gui - set_property(CACHE CMAKE_BUILD_TYPE PROPERTY STRINGS - "Debug" "Release" "MinSizeRel" "RelWithDebInfo" "Coverage") -endif() - -# Add the unity option to the cache -option(CMAKE_UNITY_BUILD "Use a unity build" OFF) - -# c++ 17 support +# ---------------------------------------------------------------------------- +# Policy +# ---------------------------------------------------------------------------- +cmake_policy(SET CMP0094 NEW) # FindPython should return the first matching Python +cmake_policy(SET CMP0135 NEW) # Suppress the warning related to the new policy on fetch content's timestamp +cmake_policy(SET CMP0148 NEW) # Suppress the warning related to the new policy on FindPythonXXX + +# ---------------------------------------------------------------------------- +# Project-level settings, options, and flags +# ---------------------------------------------------------------------------- +list(APPEND CMAKE_MODULE_PATH ${NEML2_SOURCE_DIR}/cmake/Modules) # CMake modules and macros +set_property(GLOBAL PROPERTY JOB_POOLS CI=6) # Ninja only: limit number of jobs in a CI build +set(CMAKE_CXX_FLAGS_COVERAGE "-O0 -fprofile-arcs -ftest-coverage" CACHE STRING "Flags used by C++ compiler during coverage builds." FORCE) set(CMAKE_CXX_STANDARD 17) set(CMAKE_CXX_STANDARD_REQUIRED ON) +option(NEML2_WHEELS "Modify rpath to accomodate Python wheels" OFF) +option(NEML2_TESTS "Build NEML2 tests" ON) +option(NEML2_PYBIND "Build NEML2 Python binding" OFF) +option(NEML2_DOC "Build NEML2 documentation: doxygen" OFF) -find_package(Torch) - -# Install rpath, important for a relocatable install -option(NEML2_WHEELS "Customize the build for Python wheels" OFF) - -if(NEML2_WHEELS) - if(UNIX) - if(APPLE) - set(CMAKE_INSTALL_RPATH "@loader_path;@loader_path/lib;@loader_path/../torch/lib;@loader_path/../../torch/lib") - else() - set(CMAKE_INSTALL_RPATH "$ORIGIN;$ORIGIN/lib;$ORIGIN/../torch/lib;$ORIGIN/../../torch/lib") - endif() - endif() -else() - if(UNIX) - if(APPLE) - set(CMAKE_INSTALL_RPATH "@loader_path;@loader_path/lib;${Torch_LINK_DIRECTORIES}") - else() - set(CMAKE_INSTALL_RPATH "$ORIGIN;$ORIGIN/lib;${Torch_LINK_DIRECTORIES}") - endif() - endif() +# ---------------------------------------------------------------------------- +# Dependencies and 3rd party packages +# ---------------------------------------------------------------------------- +set(PYTORCH_VERSION "2.2.2") +set(DOXYGEN_VERSION "1.10.0") +set(DOXYGEN_AWESOME_VERSION "2.3.2") +set(PYBIND11_VERSION "2.12.0") +set(HIT_VERSION "100b575af08643b5e646cac8faff6c87dd1c15a7") +set(CATCH2_VERSION "3.5.4") +set(GPERFTOOLS_VERSION "2.15") +include(NEML2Dependencies) +find_package(Torch) # This gets redirected to our FindTorch.cmake + +# ---------------------------------------------------------------------------- +# Build types +# ---------------------------------------------------------------------------- +if(NOT CMAKE_BUILD_TYPE) + set(CMAKE_BUILD_TYPE "Debug" CACHE STRING "Choose the type of build." FORCE) + set_property(CACHE CMAKE_BUILD_TYPE PROPERTY STRINGS "Debug" "Release" "MinSizeRel" "RelWithDebInfo" "Coverage") endif() -# base library -add_subdirectory(src/neml2) - -# testing -option(NEML2_TESTS "Build NEML2 tests" ON) +# ---------------------------------------------------------------------------- +# Subdirectories +# ---------------------------------------------------------------------------- +add_subdirectory(src/neml2) # base neml2 library +# tests if(NEML2_TESTS) add_subdirectory(tests) endif() -# Doxygen -option(NEML2_DOC "Build NEML2 documentation: doxygen" OFF) +# Python bindings +if(NEML2_PYBIND) + add_subdirectory(python) +endif() +# Documentation if(NEML2_DOC) add_subdirectory(doc) endif() - -# Python binding -option(NEML2_PYBIND "Build NEML2 Python binding" OFF) - -if(NEML2_PYBIND) - add_subdirectory(python) -endif() diff --git a/cmake-variants.yaml b/cmake-variants.yaml index fd60c93762..438ee0c3eb 100644 --- a/cmake-variants.yaml +++ b/cmake-variants.yaml @@ -11,10 +11,10 @@ libneml2: NEML2_REGRESSION: ON NEML2_VERIFICATION: ON NEML2_BENCHMARK: OFF - NEML2_PROFILING: OFF - profiling: + NEML2_PROFILER: OFF + benchmarking: short: Benchmarking - long: Build for benchmarking and profiling + long: Build benchmarks and profiler buildType: Release settings: NEML2_TESTS: ON @@ -22,7 +22,7 @@ libneml2: NEML2_REGRESSION: OFF NEML2_VERIFICATION: OFF NEML2_BENCHMARK: ON - NEML2_PROFILING: ON + NEML2_PROFILER: ON release: short: Release long: Build for release diff --git a/cmake/Modules/FindTorch.cmake b/cmake/Modules/FindTorch.cmake index d8741f502e..22b4bf0973 100644 --- a/cmake/Modules/FindTorch.cmake +++ b/cmake/Modules/FindTorch.cmake @@ -1,8 +1,3 @@ -set(PYTORCH_VERSION "2.2.2") - -# We will rely on FetchContent to download libTorch -include(FetchContent) - # If LIBTORCH_DIR is not defined, we should make some effort to provide a sensible default. # I have 2 plans below... @@ -25,40 +20,14 @@ endif() # Plan B: If we are on Unix systems, we could default to downloading a CPU-only # libTorch. if(NOT DEFINED LIBTORCH_DIR) - if(UNIX) - if(NOT APPLE) - FetchContent_Declare(torch URL https://download.pytorch.org/libtorch/cpu/libtorch-shared-with-deps-${PYTORCH_VERSION}%2Bcpu.zip) - else() - if(${CMAKE_SYSTEM_PROCESSOR} STREQUAL "arm64") - set(APPLE_SILICON ON) - FetchContent_Declare(torch URL https://download.pytorch.org/libtorch/cpu/libtorch-macos-arm64-${PYTORCH_VERSION}.zip) - else() - set(APPLE_SILICON OFF) - FetchContent_Declare(torch URL https://download.pytorch.org/libtorch/cpu/libtorch-macos-x86_64-${PYTORCH_VERSION}.zip) - endif() - endif() - - FetchContent_MakeAvailable(torch) - - if(NOT torch_SOURCE_DIR) - message(FATAL_ERROR "Failed to donwload libTorch") - else() - set(LIBTORCH_DIR ${torch_SOURCE_DIR}) - endif() - - else() - message(STATUS "We only download a default libTorch (CPU) on Unix systems. This is not a Unix system.") - endif() + FetchContent_MakeAvailable(torch) + set(LIBTORCH_DIR ${torch_SOURCE_DIR}) endif() # At this point, if LIBTORCH_DIR is still not set, then both plan A and plan B have failed :( if(NOT DEFINED LIBTORCH_DIR) message(FATAL_ERROR - "LIBTORCH_DIR is not set, and we could not find/download a compatible libTorch. " - "There are two ways to fix this error:\n" - " 1. Manually download libTorch and set LIBTORCH_DIR while running cmake.\n" - " 2. Install Python and the PyTorch package.\n" - "If you are on a Unix-based system and ran into this error, please submit a bug report.") + "LIBTORCH_DIR is not set. Please refer to https://reverendbedford.github.io/neml2/install.html for more information.") else() message(STATUS "Using libTorch at: ${LIBTORCH_DIR}") include(NEML2TorchConfig) diff --git a/cmake/Modules/NEML2Dependencies.cmake b/cmake/Modules/NEML2Dependencies.cmake new file mode 100644 index 0000000000..a92e9b5768 --- /dev/null +++ b/cmake/Modules/NEML2Dependencies.cmake @@ -0,0 +1,57 @@ +include(FetchContent) # For downloading dependencies + +# PyTorch +if(UNIX) + if(NOT APPLE) + FetchContent_Declare(torch URL https://download.pytorch.org/libtorch/cpu/libtorch-shared-with-deps-${PYTORCH_VERSION}%2Bcpu.zip) + else() + if(${CMAKE_SYSTEM_PROCESSOR} STREQUAL "arm64") + FetchContent_Declare(torch URL https://download.pytorch.org/libtorch/cpu/libtorch-macos-arm64-${PYTORCH_VERSION}.zip) + else() + FetchContent_Declare(torch URL https://download.pytorch.org/libtorch/cpu/libtorch-macos-x86_64-${PYTORCH_VERSION}.zip) + endif() + endif() +endif() + +# Doxygen for documentation +string(REPLACE "." "_" DOXYGEN_RELEASE ${DOXYGEN_VERSION}) +FetchContent_Declare( + doxygen + URL https://github.com/doxygen/doxygen/releases/download/Release_${DOXYGEN_RELEASE}/doxygen-${DOXYGEN_VERSION}.linux.bin.tar.gz +) + +# Doxygen html stylesheet +FetchContent_Declare( + doxygen-awesome-css + GIT_REPOSITORY https://github.com/jothepro/doxygen-awesome-css.git + GIT_TAG v${DOXYGEN_AWESOME_VERSION} +) + +# Pybind11 for Python bindings +FetchContent_Declare( + pybind11 + GIT_REPOSITORY https://github.com/pybind/pybind11.git + GIT_TAG v${PYBIND11_VERSION} +) + +# HIT for parsing input files +FetchContent_Declare( + hit + GIT_REPOSITORY https://github.com/idaholab/hit.git + GIT_TAG ${HIT_VERSION} + SOURCE_DIR ${NEML2_BINARY_DIR}/_deps/hit/hit +) + +# Catch2 for testing +FetchContent_Declare( + Catch2 + GIT_REPOSITORY https://github.com/catchorg/Catch2.git + GIT_TAG v${CATCH2_VERSION} +) + +# gperftools for profiling +FetchContent_Declare( + gperftools + GIT_REPOSITORY https://github.com/gperftools/gperftools.git + GIT_TAG gperftools-${GPERFTOOLS_VERSION} +) diff --git a/cmake/Modules/NEML2TorchConfig.cmake b/cmake/Modules/NEML2TorchConfig.cmake index 9af004ea45..320458900a 100644 --- a/cmake/Modules/NEML2TorchConfig.cmake +++ b/cmake/Modules/NEML2TorchConfig.cmake @@ -15,3 +15,22 @@ file(GLOB _TORCHLIBS ${LIBTORCH_DIR}/lib/libtorch*) set(Torch_LIBRARIES ${_C10LIBS} ${_TORCHLIBS}) find_library(Torch_PYTHON_BINDING torch_python HINTS ${Torch_LINK_DIRECTORIES}) list(REMOVE_ITEM Torch_LIBRARIES ${Torch_PYTHON_BINDING}) + +# Install rpath, important for a relocatable install +if(NEML2_WHEELS) + if(UNIX) + if(APPLE) + set(CMAKE_INSTALL_RPATH "@loader_path;@loader_path/lib;@loader_path/../torch/lib;@loader_path/../../torch/lib") + else() + set(CMAKE_INSTALL_RPATH "$ORIGIN;$ORIGIN/lib;$ORIGIN/../torch/lib;$ORIGIN/../../torch/lib") + endif() + endif() +else() + if(UNIX) + if(APPLE) + set(CMAKE_INSTALL_RPATH "@loader_path;@loader_path/lib;${Torch_LINK_DIRECTORIES}") + else() + set(CMAKE_INSTALL_RPATH "$ORIGIN;$ORIGIN/lib;${Torch_LINK_DIRECTORIES}") + endif() + endif() +endif() diff --git a/cmake/Modules/NEML2UnityGroup.cmake b/cmake/Modules/NEML2UnityGroup.cmake index fb62e9dc08..04b2d56736 100644 --- a/cmake/Modules/NEML2UnityGroup.cmake +++ b/cmake/Modules/NEML2UnityGroup.cmake @@ -1,6 +1,6 @@ -# ################################################### -# MACROs for registering UNITY groups -# ################################################### +# ---------------------------------------------------------------------------- +# Macros for registering unity groups +# ---------------------------------------------------------------------------- macro(_src_subdirs result curdir) file(GLOB_RECURSE children ${curdir}/*.cxx) set(dirlist "") @@ -14,7 +14,7 @@ macro(_src_subdirs result curdir) set(${result} ${dirlist}) endmacro() -macro(_set_unity_group name root subdir) +macro(_set_unity_group root subdir) file(RELATIVE_PATH UNITY_NAME_RAW ${root} ${subdir}) if(NOT UNITY_NAME_RAW STREQUAL "") @@ -24,13 +24,12 @@ macro(_set_unity_group name root subdir) if(NOT UNITY_NAME MATCHES "CMakeFiles") file(GLOB_RECURSE UNITY_FILES ${subdir}/*.cxx) set_source_files_properties(${UNITY_FILES} PROPERTIES UNITY_GROUP ${UNITY_NAME}) - message(STATUS "${name}: group unity for ${UNITY_NAME}") endif() endif() endif() endmacro() -macro(register_unity_group target name rel_root) +macro(register_unity_group target rel_root) get_target_property(UNITY_ENABLED ${target} UNITY_BUILD) if(UNITY_ENABLED) @@ -38,7 +37,7 @@ macro(register_unity_group target name rel_root) _src_subdirs(SUBDIRS ${ABS_ROOT}) foreach(subdir ${SUBDIRS}) - _set_unity_group(${name} ${ABS_ROOT} ${subdir}) + _set_unity_group(${ABS_ROOT} ${subdir}) endforeach() set_target_properties(${target} PROPERTIES UNITY_BUILD_MODE GROUP) diff --git a/doc/CMakeLists.txt b/doc/CMakeLists.txt index 84a6a78d38..d807c5bf91 100644 --- a/doc/CMakeLists.txt +++ b/doc/CMakeLists.txt @@ -1,83 +1,96 @@ -if(NOT DEFINED DOXYGEN_EXECUTABLE) - if(UNIX AND NOT APPLE) - FetchContent_Declare( - doxygen - URL https://github.com/doxygen/doxygen/releases/download/Release_1_10_0/doxygen-1.10.0.linux.bin.tar.gz - ) - FetchContent_MakeAvailable(doxygen) - set(DOXYGEN_EXECUTABLE ${doxygen_SOURCE_DIR}/bin/doxygen) - else() - message(FATAL_ERROR "We only download a default doxygen on linux for CI purposes. For other operating systems, please specify DOXYGEN_EXECUTABLE.") - endif() -endif() - -# Theme for the doxygen documentation -FetchContent_Declare( - doxygen-awesome-css - GIT_REPOSITORY https://github.com/jothepro/doxygen-awesome-css.git - GIT_TAG v2.3.2 -) +# ---------------------------------------------------------------------------- +# Dependencies and 3rd party packages +# ---------------------------------------------------------------------------- +FetchContent_MakeAvailable(doxygen) +set(DOXYGEN_EXECUTABLE ${doxygen_SOURCE_DIR}/bin/doxygen) FetchContent_MakeAvailable(doxygen-awesome-css) - find_package(Python COMPONENTS Interpreter) -# Generate Doxyfile for HTML -message(STATUS "Generating Doxyfile for HTML") -file(WRITE ${NEML2_BINARY_DIR}/doc/DoxyfileHTML.in "") -file(READ ${NEML2_SOURCE_DIR}/doc/config/Doxyfile.in CONTENTS) -file(APPEND ${NEML2_BINARY_DIR}/doc/DoxyfileHTML.in ${CONTENTS}) -file(READ ${NEML2_SOURCE_DIR}/doc/config/HTML.in CONTENTS) -file(APPEND ${NEML2_BINARY_DIR}/doc/DoxyfileHTML.in ${CONTENTS}) -configure_file( - ${NEML2_BINARY_DIR}/doc/DoxyfileHTML.in - ${NEML2_BINARY_DIR}/doc/DoxyfileHTML.sh -) +# ---------------------------------------------------------------------------- +# Macro for generating and configuring Doxyfile +# ---------------------------------------------------------------------------- +macro(generate_doxyfile output inputs) + file(WRITE ${output}.in "") -# Generate Doxyfile for LATEX -message(STATUS "Generating Doxyfile for LATEX") -file(WRITE ${NEML2_BINARY_DIR}/doc/DoxyfileLATEX.in "") -file(READ ${NEML2_SOURCE_DIR}/doc/config/Doxyfile.in CONTENTS) -file(APPEND ${NEML2_BINARY_DIR}/doc/DoxyfileLATEX.in ${CONTENTS}) -file(READ ${NEML2_SOURCE_DIR}/doc/config/LATEX.in CONTENTS) -file(APPEND ${NEML2_BINARY_DIR}/doc/DoxyfileLATEX.in ${CONTENTS}) -configure_file( - ${NEML2_BINARY_DIR}/doc/DoxyfileLATEX.in - ${NEML2_BINARY_DIR}/doc/DoxyfileLATEX.sh -) + foreach(input ${inputs}) + file(READ ${input} _content) + file(APPEND ${output}.in ${_content}) + endforeach() -# Build a dummy program to extract all the syntax -add_executable(syntax syntax.cxx) -target_link_libraries(syntax PRIVATE neml2) -add_custom_target(doc-syntax - DEPENDS syntax + configure_file(${output}.in ${output}.sh) + + file(REMOVE ${output}.in) +endmacro() + +# ---------------------------------------------------------------------------- +# Extract all input file syntax +# ---------------------------------------------------------------------------- +add_executable(syntax-cpp-exe syntax.cxx) +target_link_libraries(syntax-cpp-exe PRIVATE neml2) +add_custom_target(syntax-cpp + DEPENDS syntax-cpp-exe WORKING_DIRECTORY ${NEML2_BINARY_DIR}/doc - COMMAND ${NEML2_BINARY_DIR}/doc/syntax + COMMAND ${NEML2_BINARY_DIR}/doc/syntax-cpp-exe COMMAND ${Python_EXECUTABLE} ${NEML2_SOURCE_DIR}/scripts/syntax_to_md.py syntax.yml content/syntax - COMMENT "Generate NEML2 syntax and convert to markdown" VERBATIM ) -add_custom_target(doc-html ALL - DEPENDS doc-syntax +# ---------------------------------------------------------------------------- +# Extract all Python API +# ---------------------------------------------------------------------------- +if(NEML2_PYBIND) + add_custom_target(syntax-python + DEPENDS base tensors models math + WORKING_DIRECTORY ${NEML2_BINARY_DIR}/python + COMMAND ${CMAKE_COMMAND} -E make_directory ${NEML2_BINARY_DIR}/doc/content/python + COMMAND PYTHONPATH=. pybind11-stubgen -o ${NEML2_BINARY_DIR}/doc/content/python neml2 + COMMAND PYTHONPATH=. pybind11-stubgen -o ${NEML2_BINARY_DIR}/doc/content/python neml2.math + VERBATIM + ) +endif() + +# ---------------------------------------------------------------------------- +# HTML +# ---------------------------------------------------------------------------- +generate_doxyfile(${NEML2_BINARY_DIR}/doc/DoxyfileHTML "config/Doxyfile.in;config/HTML.in") +add_custom_target(html ALL + DEPENDS syntax-cpp WORKING_DIRECTORY ${NEML2_BINARY_DIR}/doc COMMAND ${DOXYGEN_EXECUTABLE} -q DoxyfileHTML.sh - COMMENT "Generate Doxygen HTML" VERBATIM ) -add_custom_target(doc-latex - DEPENDS doc-syntax +# ---------------------------------------------------------------------------- +# HTML (PYTHON) +# ---------------------------------------------------------------------------- +if(NEML2_PYBIND) + generate_doxyfile(${NEML2_BINARY_DIR}/doc/DoxyfilePython "config/Doxyfile.in;config/HTML.in;config/Python.in") + add_custom_target(html-python ALL + DEPENDS syntax-python + WORKING_DIRECTORY ${NEML2_BINARY_DIR}/doc + COMMAND ${DOXYGEN_EXECUTABLE} -q DoxyfilePython.sh + VERBATIM + ) +endif() + +# ---------------------------------------------------------------------------- +# LaTeX +# ---------------------------------------------------------------------------- +generate_doxyfile(${NEML2_BINARY_DIR}/doc/DoxyfileLaTeX "config/Doxyfile.in;config/LaTeX.in") +add_custom_target(latex + DEPENDS syntax-cpp WORKING_DIRECTORY ${NEML2_BINARY_DIR}/doc - COMMAND ${DOXYGEN_EXECUTABLE} -q DoxyfileLATEX.sh - COMMENT "Generate Doxygen LATEX" + COMMAND ${DOXYGEN_EXECUTABLE} -q DoxyfileLaTeX.sh VERBATIM ) -add_custom_target(doc-pdf - DEPENDS doc-latex +# ---------------------------------------------------------------------------- +# PDF +# ---------------------------------------------------------------------------- +add_custom_target(pdf + DEPENDS latex WORKING_DIRECTORY ${NEML2_BINARY_DIR}/doc/build/latex COMMAND ${CMAKE_COMMAND} -E copy_directory ${NEML2_SOURCE_DIR}/doc/content/asset ${NEML2_BINARY_DIR}/doc/build/latex COMMAND latexmk -pdf refman.tex -outdir=${NEML2_BINARY_DIR}/doc/build/pdf -silent - COMMENT "Generate Doxygen PDF" VERBATIM ) diff --git a/doc/config/Doxyfile.in b/doc/config/Doxyfile.in index c73bb9bc3e..536cdd5def 100644 --- a/doc/config/Doxyfile.in +++ b/doc/config/Doxyfile.in @@ -24,7 +24,6 @@ LAYOUT_FILE = ${NEML2_SOURCE_DIR}/doc/config/DoxygenLayout.xml # Configuration options related to warning and progress messages #--------------------------------------------------------------------------- WARN_NO_PARAMDOC = YES -WARN_LOGFILE = ${NEML2_BINARY_DIR}/doc/doxygen.log #--------------------------------------------------------------------------- # Configuration options related to the input files @@ -36,9 +35,7 @@ INPUT = ${NEML2_SOURCE_DIR}/README.md \ ${NEML2_BINARY_DIR}/doc/content FILE_PATTERNS = *.cxx \ *.h \ - *.pyi \ *.md -EXTENSION_MAPPING = pyi=Python RECURSIVE = YES IMAGE_PATH = ${NEML2_SOURCE_DIR}/doc/content/asset EXCLUDE_SYMBOLS = neml2::*internal neml2::*details diff --git a/doc/config/DoxygenLayout.xml b/doc/config/DoxygenLayout.xml index af4a93ea5d..354c223a86 100644 --- a/doc/config/DoxygenLayout.xml +++ b/doc/config/DoxygenLayout.xml @@ -22,7 +22,8 @@ - + + diff --git a/doc/config/DoxygenLayoutPython.xml b/doc/config/DoxygenLayoutPython.xml new file mode 100644 index 0000000000..d678dc5104 --- /dev/null +++ b/doc/config/DoxygenLayoutPython.xml @@ -0,0 +1,228 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/doc/config/HTML.in b/doc/config/HTML.in index 0d9ace4574..8bf5334601 100644 --- a/doc/config/HTML.in +++ b/doc/config/HTML.in @@ -14,3 +14,4 @@ MATHJAX_VERSION = MathJax_3 MATHJAX_FORMAT = HTML-CSS MATHJAX_RELPATH = https://cdn.jsdelivr.net/npm/mathjax@3 MATHJAX_EXTENSIONS = ams physics boldsymbol +WARN_LOGFILE = ${NEML2_BINARY_DIR}/doc/doxygen.html.log diff --git a/doc/config/LATEX.in b/doc/config/LaTeX.in similarity index 92% rename from doc/config/LATEX.in rename to doc/config/LaTeX.in index c352ea6c57..27c85f78f5 100644 --- a/doc/config/LATEX.in +++ b/doc/config/LaTeX.in @@ -8,6 +8,7 @@ LATEX_HEADER = ${NEML2_SOURCE_DIR}/doc/config/ANLReportHeader.tex LATEX_FOOTER = ${NEML2_SOURCE_DIR}/doc/config/ANLReportFooter.tex LATEX_EXTRA_STYLESHEET = ${NEML2_SOURCE_DIR}/doc/config/ANLReportExtra.sty LATEX_HIDE_INDICES = YES +WARN_LOGFILE = ${NEML2_BINARY_DIR}/doc/doxygen.latex.log #--------------------------------------------------------------------------- # Configuration options related to the dot tool diff --git a/doc/config/Python.in b/doc/config/Python.in new file mode 100644 index 0000000000..73afe6cc3c --- /dev/null +++ b/doc/config/Python.in @@ -0,0 +1,8 @@ +#--------------------------------------------------------------------------- +# Configuration options related to the HTML output for Python bindings +#--------------------------------------------------------------------------- +HTML_OUTPUT = html/python +FILE_PATTERNS = *.pyi +EXTENSION_MAPPING = pyi=Python +WARN_LOGFILE = ${NEML2_BINARY_DIR}/doc/doxygen.python.log +LAYOUT_FILE = ${NEML2_SOURCE_DIR}/doc/config/DoxygenLayoutPython.xml diff --git a/doc/content/install.md b/doc/content/install.md index b6e3a6bd69..63b589bc30 100644 --- a/doc/content/install.md +++ b/doc/content/install.md @@ -94,8 +94,8 @@ Commonly used configuration options are summarized below. Default options are un | NEML2_UNIT | ON, OFF | Create the unit testing target | | NEML2_REGRESSION | ON, OFF | Create the regression testing target | | NEML2_VERIFICATION | ON, OFF | Create the verification testing target | -| NEML2_BENCHMARK | ON, OFF | Create the benchmark testing target | -| NEML2_PROFILING | ON, OFF | Create the profiling executable target | +| NEML2_BENCHMARK | ON, OFF | Create the benchmark target | +| NEML2_PROFILER | ON, OFF | Create the profiler target | | NEML2_DOC | ON, OFF | Create the documentation target | | NEML2_PYBIND | ON, OFF | Create the Python bindings target | diff --git a/doc/requirements.txt b/doc/requirements.txt index 5500f007d0..df8a651c87 100644 --- a/doc/requirements.txt +++ b/doc/requirements.txt @@ -1 +1,2 @@ PyYAML +pybind11-stubgen diff --git a/python/CMakeLists.txt b/python/CMakeLists.txt index 595b1af1cd..40564b8cf2 100644 --- a/python/CMakeLists.txt +++ b/python/CMakeLists.txt @@ -1,23 +1,10 @@ -# pybind11 -FetchContent_Declare( - pybind11 - GIT_REPOSITORY https://github.com/pybind/pybind11.git - GIT_TAG v2.12.0 -) FetchContent_MakeAvailable(pybind11) if(NOT Torch_PYTHON_BINDING) message(FATAL_ERROR "Could not find the libTorch Python binding") endif() -if(NOT ${NEML2_BINARY_DIR} STREQUAL ${NEML2_SOURCE_DIR}) - add_custom_target(pyneml2 ALL - COMMAND ${CMAKE_COMMAND} -E copy ${NEML2_SOURCE_DIR}/python/neml2/__init__.py ${NEML2_BINARY_DIR}/python/neml2/__init__.py - COMMENT "Copying __init__.py" - ) -endif() - -# macro for defining a submodule with given source files +# Macro for defining a submodule with given source files macro(add_submodule mname msrcs) pybind11_add_module(${mname} MODULE ${msrcs}) set_target_properties(${mname} PROPERTIES LIBRARY_OUTPUT_DIRECTORY neml2) @@ -39,5 +26,19 @@ add_submodule_dir(tensors) add_submodule_dir(models) add_submodule(math neml2/misc/math.cxx) -# Install python artifacts +# Artifacts +configure_file(neml2/__init__.py ${NEML2_BINARY_DIR}/python/neml2/__init__.py COPYONLY) install(FILES neml2/__init__.py DESTINATION .) + +# Tests +if(NEML2_TESTS) + if(NOT ${NEML2_BINARY_DIR} STREQUAL ${NEML2_SOURCE_DIR}) + file(CREATE_LINK ${NEML2_SOURCE_DIR}/tests/python ${NEML2_BINARY_DIR}/tests/python SYMBOLIC) + endif() + + install(DIRECTORY python + TYPE BIN + FILES_MATCHING + PATTERN "*.py" + ) +endif() diff --git a/scripts/benchmark.sh b/scripts/benchmark.sh index 90a9c161e0..6d076fccd3 100755 --- a/scripts/benchmark.sh +++ b/scripts/benchmark.sh @@ -26,6 +26,6 @@ cd tests mkdir -p $3 -./benchmark_tests $1 --benchmark-samples $2 --use-colour no -o $3/log.txt +./benchmark $1 --benchmark-samples $2 --use-colour no -o $3/log.txt python ../scripts/extract_timings.py $3/log.txt $3/timings.csv python ../scripts/analyze_timings.py $3/timings.csv $3/timings.png diff --git a/src/neml2/CMakeLists.txt b/src/neml2/CMakeLists.txt index 975b86f885..4ce83e15f2 100644 --- a/src/neml2/CMakeLists.txt +++ b/src/neml2/CMakeLists.txt @@ -53,12 +53,6 @@ target_link_directories(neml2 PUBLIC ${Torch_LINK_DIRECTORIES}) target_link_libraries(neml2 PUBLIC ${Torch_LIBRARIES}) # hit for parsing -FetchContent_Declare( - hit - GIT_REPOSITORY https://github.com/idaholab/hit.git - GIT_TAG 100b575af08643b5e646cac8faff6c87dd1c15a7 - SOURCE_DIR ${NEML2_BINARY_DIR}/_deps/hit/hit -) FetchContent_MakeAvailable(hit) add_library(hit SHARED diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt index 8afc275efc..992f6ecf48 100644 --- a/tests/CMakeLists.txt +++ b/tests/CMakeLists.txt @@ -1,15 +1,19 @@ -include(NEML2UnityGroup) +# ---------------------------------------------------------------------------- +# Project-level settings, options, and flags +# ---------------------------------------------------------------------------- +option(NEML2_UNIT "Build NEML2 unit tests" ON) +option(NEML2_REGRESSION "Build NEML2 regression tests" ON) +option(NEML2_VERIFICATION "Build NEML2 verification tests" ON) +option(NEML2_BENCHMARK "Build NEML2 benchmark tests" OFF) +option(NEML2_PROFILER "Build NEML2 profiler" OFF) +# ---------------------------------------------------------------------------- +# Dependencies and 3rd party packages +# ---------------------------------------------------------------------------- if(NEML2_UNIT OR NEML2_REGRESSION OR NEML2_VERIFICATION OR NEML2_BENCHMARK) - # Catch2, for testing - FetchContent_Declare( - Catch2 - GIT_REPOSITORY https://github.com/catchorg/Catch2.git - GIT_TAG v3.5.4 - ) FetchContent_MakeAvailable(Catch2) - # Catch2 v3 is built as a static library, so we MUST make sure the compile definitions are compatible with ours + # Catch2 is built as a static library, so we MUST make sure the compile definitions are compatible with ours if(Torch_CXX11_ABI) target_compile_definitions(Catch2 PUBLIC _GLIBCXX_USE_CXX11_ABI=1) else() @@ -17,214 +21,38 @@ if(NEML2_UNIT OR NEML2_REGRESSION OR NEML2_VERIFICATION OR NEML2_BENCHMARK) endif() endif() +# ---------------------------------------------------------------------------- +# Subdirectories +# ---------------------------------------------------------------------------- # Test utilities add_subdirectory(src) -# ################################################### # Unit tests -# ################################################### -option(NEML2_UNIT "Build NEML2 unit tests" ON) - if(NEML2_UNIT) - file(GLOB_RECURSE UNIT_TESTS unit/*.cxx) - add_executable(unit_tests ${UNIT_TESTS}) - - target_compile_options(unit_tests PRIVATE -Wall -Wextra -pedantic -Werror) - register_unity_group(unit_tests "Unit test" unit) - target_link_libraries(unit_tests PRIVATE testutils) - target_link_libraries(unit_tests PRIVATE Catch2::Catch2WithMain) - - if(NOT ${NEML2_BINARY_DIR} STREQUAL ${NEML2_SOURCE_DIR}) - add_custom_command(TARGET unit_tests - POST_BUILD - COMMAND ${CMAKE_COMMAND} -E create_symlink ${NEML2_SOURCE_DIR}/tests/unit ${NEML2_BINARY_DIR}/tests/unit - COMMENT "Creating symlink for unit tests" - ) - endif() - - install(TARGETS unit_tests) - install(DIRECTORY unit - TYPE BIN - FILES_MATCHING - PATTERN "*.i" - PATTERN "*.pt" - PATTERN "*.txt" - PATTERN "*.vtest" - PATTERN "*.xml" - PATTERN "*.py" - PATTERN "*.pyi" - ) + add_subdirectory(unit build/unit) endif() -# ################################################### # Regression tests -# ################################################### -option(NEML2_REGRESSION "Build NEML2 regression tests" ON) - if(NEML2_REGRESSION) - file(GLOB_RECURSE REGRESSION_TESTS regression/*.cxx) - add_executable(regression_tests ${REGRESSION_TESTS}) - - target_compile_options(regression_tests PRIVATE -Wall -Wextra -pedantic -Werror) - register_unity_group(regression_tests "Regression test" regression) - target_link_libraries(regression_tests PRIVATE testutils) - target_link_libraries(regression_tests PRIVATE Catch2::Catch2WithMain) - - if(NOT ${NEML2_BINARY_DIR} STREQUAL ${NEML2_SOURCE_DIR}) - add_custom_command(TARGET regression_tests - POST_BUILD - COMMAND ${CMAKE_COMMAND} -E create_symlink ${NEML2_SOURCE_DIR}/tests/regression ${NEML2_BINARY_DIR}/tests/regression - COMMENT "Creating symlink for regression tests" - ) - endif() - - install(TARGETS regression_tests) - install(DIRECTORY regression - TYPE BIN - FILES_MATCHING - PATTERN "*.i" - PATTERN "*.pt" - PATTERN "*.txt" - PATTERN "*.vtest" - PATTERN "*.xml" - PATTERN "*.py" - PATTERN "*.pyi" - ) + add_subdirectory(regression build/regression) endif() -# ################################################### # Verification tests -# ################################################### -option(NEML2_VERIFICATION "Build NEML2 verification tests" ON) - if(NEML2_VERIFICATION) - file(GLOB_RECURSE VERIFICATION_TESTS verification/*.cxx) - add_executable(verification_tests ${VERIFICATION_TESTS}) - - target_compile_options(verification_tests PRIVATE -Wall -Wextra -pedantic -Werror) - register_unity_group(verification_tests "Verification test" verification) - target_link_libraries(verification_tests PRIVATE testutils) - target_link_libraries(verification_tests PRIVATE Catch2::Catch2WithMain) - - if(NOT ${NEML2_BINARY_DIR} STREQUAL ${NEML2_SOURCE_DIR}) - add_custom_command(TARGET verification_tests - POST_BUILD - COMMAND ${CMAKE_COMMAND} -E create_symlink ${NEML2_SOURCE_DIR}/tests/verification ${NEML2_BINARY_DIR}/tests/verification - COMMENT "Creating symlink for verification tests" - ) - endif() - - install(TARGETS verification_tests) - install(DIRECTORY verification - TYPE BIN - FILES_MATCHING - PATTERN "*.i" - PATTERN "*.pt" - PATTERN "*.txt" - PATTERN "*.vtest" - PATTERN "*.xml" - PATTERN "*.py" - PATTERN "*.pyi" - ) + add_subdirectory(verification build/verification) endif() -# ################################################### -# Benchmarks -# ################################################### -option(NEML2_BENCHMARK "Build NEML2 benchmark tests" OFF) - +# Benchmark tests if(NEML2_BENCHMARK) - file(GLOB_RECURSE BENCHMARK_TESTS benchmark/*.cxx) - add_executable(benchmark_tests ${BENCHMARK_TESTS}) - - # compile options - target_compile_options(benchmark_tests PRIVATE -Wall -Wextra -pedantic -Werror) - - register_unity_group(benchmark_tests "Benchmark test" benchmark) - target_link_libraries(benchmark_tests PRIVATE testutils) - target_link_libraries(benchmark_tests PRIVATE Catch2::Catch2WithMain) - - if(NOT ${NEML2_BINARY_DIR} STREQUAL ${NEML2_SOURCE_DIR}) - add_custom_command(TARGET benchmark_tests - POST_BUILD - COMMAND ${CMAKE_COMMAND} -E create_symlink ${NEML2_SOURCE_DIR}/tests/benchmark ${NEML2_BINARY_DIR}/tests/benchmark - COMMENT "Creating symlink for benchmark tests" - ) - endif() - - install(TARGETS benchmark_tests) - install(DIRECTORY benchmark - TYPE BIN - FILES_MATCHING - PATTERN "*.i" - PATTERN "*.pt" - PATTERN "*.txt" - PATTERN "*.vtest" - PATTERN "*.xml" - PATTERN "*.py" - PATTERN "*.pyi" - ) + add_subdirectory(benchmark build/benchmark) endif() -# ################################################### -# Profiling -# ################################################### -option(NEML2_PROFILING "Build NEML2 profiling tests" OFF) - -if(NEML2_PROFILING) - # gperftools - FetchContent_Declare( - gperftools - GIT_REPOSITORY https://github.com/gperftools/gperftools.git - GIT_TAG gperftools-2.15 - ) - FetchContent_Populate(gperftools) - add_subdirectory(${gperftools_SOURCE_DIR} ${gperftools_BINARY_DIR} EXCLUDE_FROM_ALL) - - file(GLOB_RECURSE PROFILING_TESTS profiling/*.cxx) - add_executable(profiling_tests ${PROFILING_TESTS}) - - register_unity_group(profiling_tests "Profiling test" profiling) - target_compile_options(profiling_tests PRIVATE -Wall -Wextra -pedantic -Werror) - target_link_options(profiling_tests PRIVATE "-Wl,-no-as-needed") - target_link_libraries(profiling_tests PRIVATE testutils profiler) - - if(NOT ${NEML2_BINARY_DIR} STREQUAL ${NEML2_SOURCE_DIR}) - add_custom_command(TARGET profiling_tests - POST_BUILD - COMMAND ${CMAKE_COMMAND} -E create_symlink ${NEML2_SOURCE_DIR}/tests/profiling ${NEML2_BINARY_DIR}/tests/profiling - COMMENT "Creating symlink for profiling tests" - ) - endif() - - install(TARGETS profiling_tests) - install(DIRECTORY profiling - TYPE BIN - FILES_MATCHING - PATTERN "*.i" - PATTERN "*.pt" - PATTERN "*.txt" - PATTERN "*.vtest" - PATTERN "*.xml" - PATTERN "*.py" - PATTERN "*.pyi" - ) +# Profiler +if(NEML2_PROFILER) + add_subdirectory(profiler build/profiler) endif() -# ################################################### -# Python binding tests -# ################################################### +# pytests if(NEML2_PYBIND) - if(NOT ${NEML2_BINARY_DIR} STREQUAL ${NEML2_SOURCE_DIR}) - add_custom_target(link_pybind_tests ALL - COMMAND ${CMAKE_COMMAND} -E create_symlink ${NEML2_SOURCE_DIR}/tests/python ${NEML2_BINARY_DIR}/tests/python - COMMENT "Creating symlink for python binding tests" - ) - endif() - - install(DIRECTORY python - TYPE BIN - FILES_MATCHING - PATTERN "*.py" - ) + add_subdirectory(python build/python) endif() diff --git a/tests/benchmark/CMakeLists.txt b/tests/benchmark/CMakeLists.txt new file mode 100644 index 0000000000..e01c6d4207 --- /dev/null +++ b/tests/benchmark/CMakeLists.txt @@ -0,0 +1,26 @@ +include(NEML2UnityGroup) + +file(GLOB_RECURSE srcs *.cxx) +add_executable(benchmark ${srcs}) + +target_compile_options(benchmark PRIVATE -Wall -Wextra -pedantic -Werror) +register_unity_group(benchmark .) +target_link_libraries(benchmark PRIVATE testutils) +target_link_libraries(benchmark PRIVATE Catch2::Catch2WithMain) + +if(NOT ${NEML2_BINARY_DIR} STREQUAL ${NEML2_SOURCE_DIR}) + file(CREATE_LINK ${NEML2_SOURCE_DIR}/tests/benchmark ${NEML2_BINARY_DIR}/tests/benchmark SYMBOLIC) +endif() + +install(TARGETS benchmark) +install(DIRECTORY . + TYPE BIN + FILES_MATCHING + PATTERN "*.i" + PATTERN "*.pt" + PATTERN "*.txt" + PATTERN "*.vtest" + PATTERN "*.xml" + PATTERN "*.py" + PATTERN "*.pyi" +) diff --git a/tests/profiler/CMakeLists.txt b/tests/profiler/CMakeLists.txt new file mode 100644 index 0000000000..6ac56e820b --- /dev/null +++ b/tests/profiler/CMakeLists.txt @@ -0,0 +1,28 @@ +include(NEML2UnityGroup) + +FetchContent_Populate(gperftools) +add_subdirectory(${gperftools_SOURCE_DIR} ${gperftools_BINARY_DIR} EXCLUDE_FROM_ALL) + +file(GLOB_RECURSE srcs *.cxx) +add_executable(profiler ${srcs}) +register_unity_group(profiler .) +target_compile_options(profiler PRIVATE -Wall -Wextra -pedantic -Werror) +target_link_options(profiler PRIVATE "-Wl,-no-as-needed") +target_link_libraries(profiler PRIVATE testutils profiler) + +if(NOT ${NEML2_BINARY_DIR} STREQUAL ${NEML2_SOURCE_DIR}) + file(CREATE_LINK ${NEML2_SOURCE_DIR}/tests/profiler ${NEML2_BINARY_DIR}/tests/profiler SYMBOLIC) +endif() + +install(TARGETS profiler) +install(DIRECTORY . + TYPE BIN + FILES_MATCHING + PATTERN "*.i" + PATTERN "*.pt" + PATTERN "*.txt" + PATTERN "*.vtest" + PATTERN "*.xml" + PATTERN "*.py" + PATTERN "*.pyi" +) diff --git a/tests/profiling/main.cxx b/tests/profiler/main.cxx similarity index 95% rename from tests/profiling/main.cxx rename to tests/profiler/main.cxx index 668cd39ece..a2befb897c 100644 --- a/tests/profiling/main.cxx +++ b/tests/profiler/main.cxx @@ -33,7 +33,7 @@ main(int argc, char * argv[]) { if (argc != 3) { - std::cout << "Usage: profiling_tests /path/to/input.i driver_name" << std::endl; + std::cout << "Usage: profiler /path/to/input.i driver_name" << std::endl; return 1; } diff --git a/tests/profiling/model.i b/tests/profiler/model.i similarity index 100% rename from tests/profiling/model.i rename to tests/profiler/model.i diff --git a/tests/python/CMakeLists.txt b/tests/python/CMakeLists.txt new file mode 100644 index 0000000000..bf7f1bc72b --- /dev/null +++ b/tests/python/CMakeLists.txt @@ -0,0 +1,9 @@ +if(NOT ${NEML2_BINARY_DIR} STREQUAL ${NEML2_SOURCE_DIR}) + file(CREATE_LINK ${NEML2_SOURCE_DIR}/tests/python ${NEML2_BINARY_DIR}/tests/python SYMBOLIC) +endif() + +install(DIRECTORY . + TYPE BIN + FILES_MATCHING + PATTERN "*.py" +) diff --git a/tests/regression/CMakeLists.txt b/tests/regression/CMakeLists.txt new file mode 100644 index 0000000000..c5c0e3a6e0 --- /dev/null +++ b/tests/regression/CMakeLists.txt @@ -0,0 +1,27 @@ +include(NEML2UnityGroup) + +file(GLOB_RECURSE srcs *.cxx) +add_executable(regression_tests ${srcs}) +set_target_properties(regression_tests PROPERTIES RUNTIME_OUTPUT_DIRECTORY ${NEML2_BINARY_DIR}/tests) + +target_compile_options(regression_tests PRIVATE -Wall -Wextra -pedantic -Werror) +register_unity_group(regression_tests .) +target_link_libraries(regression_tests PRIVATE testutils) +target_link_libraries(regression_tests PRIVATE Catch2::Catch2WithMain) + +if(NOT ${NEML2_BINARY_DIR} STREQUAL ${NEML2_SOURCE_DIR}) + file(CREATE_LINK ${NEML2_SOURCE_DIR}/tests/regression ${NEML2_BINARY_DIR}/tests/regression SYMBOLIC) +endif() + +install(TARGETS regression_tests) +install(DIRECTORY . + TYPE BIN + FILES_MATCHING + PATTERN "*.i" + PATTERN "*.pt" + PATTERN "*.txt" + PATTERN "*.vtest" + PATTERN "*.xml" + PATTERN "*.py" + PATTERN "*.pyi" +) diff --git a/tests/unit/CMakeLists.txt b/tests/unit/CMakeLists.txt new file mode 100644 index 0000000000..04df62bbdf --- /dev/null +++ b/tests/unit/CMakeLists.txt @@ -0,0 +1,27 @@ +include(NEML2UnityGroup) + +file(GLOB_RECURSE srcs *.cxx) +add_executable(unit_tests ${srcs}) +set_target_properties(unit_tests PROPERTIES RUNTIME_OUTPUT_DIRECTORY ${NEML2_BINARY_DIR}/tests) + +target_compile_options(unit_tests PRIVATE -Wall -Wextra -pedantic -Werror) +register_unity_group(unit_tests .) +target_link_libraries(unit_tests PRIVATE testutils) +target_link_libraries(unit_tests PRIVATE Catch2::Catch2WithMain) + +if(NOT ${NEML2_BINARY_DIR} STREQUAL ${NEML2_SOURCE_DIR}) + file(CREATE_LINK ${NEML2_SOURCE_DIR}/tests/unit ${NEML2_BINARY_DIR}/tests/unit SYMBOLIC) +endif() + +install(TARGETS unit_tests) +install(DIRECTORY . + TYPE BIN + FILES_MATCHING + PATTERN "*.i" + PATTERN "*.pt" + PATTERN "*.txt" + PATTERN "*.vtest" + PATTERN "*.xml" + PATTERN "*.py" + PATTERN "*.pyi" +) diff --git a/tests/verification/CMakeLists.txt b/tests/verification/CMakeLists.txt new file mode 100644 index 0000000000..41f4c53749 --- /dev/null +++ b/tests/verification/CMakeLists.txt @@ -0,0 +1,27 @@ +include(NEML2UnityGroup) + +file(GLOB_RECURSE srcs *.cxx) +add_executable(verification_tests ${srcs}) +set_target_properties(verification_tests PROPERTIES RUNTIME_OUTPUT_DIRECTORY ${NEML2_BINARY_DIR}/tests) + +target_compile_options(verification_tests PRIVATE -Wall -Wextra -pedantic -Werror) +register_unity_group(verification_tests .) +target_link_libraries(verification_tests PRIVATE testutils) +target_link_libraries(verification_tests PRIVATE Catch2::Catch2WithMain) + +if(NOT ${NEML2_BINARY_DIR} STREQUAL ${NEML2_SOURCE_DIR}) + file(CREATE_LINK ${NEML2_SOURCE_DIR}/tests/verification ${NEML2_BINARY_DIR}/tests/verification SYMBOLIC) +endif() + +install(TARGETS verification_tests) +install(DIRECTORY . + TYPE BIN + FILES_MATCHING + PATTERN "*.i" + PATTERN "*.pt" + PATTERN "*.txt" + PATTERN "*.vtest" + PATTERN "*.xml" + PATTERN "*.py" + PATTERN "*.pyi" +) From b782076e597a2f9a9e57fd9d870030f12da23a76 Mon Sep 17 00:00:00 2001 From: Gary Hu Date: Fri, 17 May 2024 15:37:42 -0500 Subject: [PATCH 28/55] Fix workflow build command; Fix test targets output directory --- .github/workflows/build_docs.yml | 8 +-- cmake-variants.yaml | 38 +++--------- cmake/Modules/NEML2UnityGroup.cmake | 1 - doc/CMakeLists.txt | 35 ++++------- doc/DoxyfileHTML.sh | 86 ++++++++++++++++++++++++++ doc/DoxyfileLaTeX.sh | 85 ++++++++++++++++++++++++++ doc/DoxyfilePython.sh | 94 +++++++++++++++++++++++++++++ src/neml2/CMakeLists.txt | 2 +- 8 files changed, 286 insertions(+), 63 deletions(-) create mode 100644 doc/DoxyfileHTML.sh create mode 100644 doc/DoxyfileLaTeX.sh create mode 100644 doc/DoxyfilePython.sh diff --git a/.github/workflows/build_docs.yml b/.github/workflows/build_docs.yml index 61e6410083..9ce01857f9 100644 --- a/.github/workflows/build_docs.yml +++ b/.github/workflows/build_docs.yml @@ -35,15 +35,11 @@ jobs: cmake \ -DCMAKE_BUILD_TYPE=Release \ -DCMAKE_UNITY_BUILD=ON \ - -DNEML2_UNIT=OFF \ - -DNEML2_REGRESSION=OFF \ - -DNEML2_VERIFICATION=OFF \ - -DNEML2_BENCHMARK=OFF \ - -DNEML2_PROFILER=OFF \ + -DNEML2_TESTS=OFF \ -DNEML2_PYBIND=ON \ -DNEML2_DOC=ON \ . - - run: make doc-html -j 2 + - run: cmake --build . --target html -j 2 -- - run: cat doc/doxygen.log - name: Preview GitHub Pages if: ${{ github.event_name == 'pull_request' }} diff --git a/cmake-variants.yaml b/cmake-variants.yaml index 438ee0c3eb..5d37682090 100644 --- a/cmake-variants.yaml +++ b/cmake-variants.yaml @@ -12,6 +12,8 @@ libneml2: NEML2_VERIFICATION: ON NEML2_BENCHMARK: OFF NEML2_PROFILER: OFF + NEML2_PYBIND: ON + NEML2_DOC: ON benchmarking: short: Benchmarking long: Build benchmarks and profiler @@ -23,39 +25,13 @@ libneml2: NEML2_VERIFICATION: OFF NEML2_BENCHMARK: ON NEML2_PROFILER: ON - release: - short: Release - long: Build for release + NEML2_PYBIND: OFF + NEML2_DOC: OFF + production: + short: Production + long: Build for production release buildType: RelWithDebInfo settings: NEML2_TESTS: OFF - -pyneml2: - default: none - choices: - none: - short: No pybind - long: Not building python binding - settings: - NEML2_PYBIND: OFF - NEML2_WHEELS: OFF - binding: - short: Pybind - long: Build python binding - settings: NEML2_PYBIND: ON - NEML2_WHEELS: OFF - -doc: - default: no - choices: - no: - short: No doc - long: No documentation target - settings: - NEML2_DOC: OFF - yes: - short: Doc - long: Add documentation targets - settings: NEML2_DOC: ON diff --git a/cmake/Modules/NEML2UnityGroup.cmake b/cmake/Modules/NEML2UnityGroup.cmake index 04b2d56736..83aaed4e99 100644 --- a/cmake/Modules/NEML2UnityGroup.cmake +++ b/cmake/Modules/NEML2UnityGroup.cmake @@ -3,7 +3,6 @@ # ---------------------------------------------------------------------------- macro(_src_subdirs result curdir) file(GLOB_RECURSE children ${curdir}/*.cxx) - set(dirlist "") foreach(child ${children}) get_filename_component(child_dir ${child} DIRECTORY) diff --git a/doc/CMakeLists.txt b/doc/CMakeLists.txt index d807c5bf91..681919e707 100644 --- a/doc/CMakeLists.txt +++ b/doc/CMakeLists.txt @@ -38,41 +38,28 @@ add_custom_target(syntax-cpp # ---------------------------------------------------------------------------- # Extract all Python API # ---------------------------------------------------------------------------- -if(NEML2_PYBIND) - add_custom_target(syntax-python - DEPENDS base tensors models math - WORKING_DIRECTORY ${NEML2_BINARY_DIR}/python - COMMAND ${CMAKE_COMMAND} -E make_directory ${NEML2_BINARY_DIR}/doc/content/python - COMMAND PYTHONPATH=. pybind11-stubgen -o ${NEML2_BINARY_DIR}/doc/content/python neml2 - COMMAND PYTHONPATH=. pybind11-stubgen -o ${NEML2_BINARY_DIR}/doc/content/python neml2.math - VERBATIM - ) -endif() +add_custom_target(syntax-python + DEPENDS base tensors models math + WORKING_DIRECTORY ${NEML2_BINARY_DIR}/python + COMMAND ${CMAKE_COMMAND} -E make_directory ${NEML2_BINARY_DIR}/doc/content/python + COMMAND PYTHONPATH=. pybind11-stubgen -o ${NEML2_BINARY_DIR}/doc/content/python neml2 + COMMAND PYTHONPATH=. pybind11-stubgen -o ${NEML2_BINARY_DIR}/doc/content/python neml2.math + VERBATIM +) # ---------------------------------------------------------------------------- # HTML # ---------------------------------------------------------------------------- generate_doxyfile(${NEML2_BINARY_DIR}/doc/DoxyfileHTML "config/Doxyfile.in;config/HTML.in") +generate_doxyfile(${NEML2_BINARY_DIR}/doc/DoxyfilePython "config/Doxyfile.in;config/HTML.in;config/Python.in") add_custom_target(html ALL - DEPENDS syntax-cpp + DEPENDS syntax-cpp syntax-python WORKING_DIRECTORY ${NEML2_BINARY_DIR}/doc COMMAND ${DOXYGEN_EXECUTABLE} -q DoxyfileHTML.sh + COMMAND ${DOXYGEN_EXECUTABLE} -q DoxyfilePython.sh VERBATIM ) -# ---------------------------------------------------------------------------- -# HTML (PYTHON) -# ---------------------------------------------------------------------------- -if(NEML2_PYBIND) - generate_doxyfile(${NEML2_BINARY_DIR}/doc/DoxyfilePython "config/Doxyfile.in;config/HTML.in;config/Python.in") - add_custom_target(html-python ALL - DEPENDS syntax-python - WORKING_DIRECTORY ${NEML2_BINARY_DIR}/doc - COMMAND ${DOXYGEN_EXECUTABLE} -q DoxyfilePython.sh - VERBATIM - ) -endif() - # ---------------------------------------------------------------------------- # LaTeX # ---------------------------------------------------------------------------- diff --git a/doc/DoxyfileHTML.sh b/doc/DoxyfileHTML.sh new file mode 100644 index 0000000000..35e340094e --- /dev/null +++ b/doc/DoxyfileHTML.sh @@ -0,0 +1,86 @@ +# Doxyfile 1.9.8 + +#--------------------------------------------------------------------------- +# Project related configuration options +#--------------------------------------------------------------------------- +DOXYFILE_ENCODING = UTF-8 +PROJECT_NAME = NEML2 +PROJECT_NUMBER = 1.4.0 +OUTPUT_DIRECTORY = build +TOC_INCLUDE_HEADINGS = 3 + +#--------------------------------------------------------------------------- +# Build related configuration options +#--------------------------------------------------------------------------- +EXTRACT_ALL = YES +CASE_SENSE_NAMES = YES +HIDE_SCOPE_NAMES = YES +SHOW_USED_FILES = NO +SHOW_FILES = NO +SHOW_NAMESPACES = NO +LAYOUT_FILE = /home/thu/projects/neml2/doc/config/DoxygenLayout.xml + +#--------------------------------------------------------------------------- +# Configuration options related to warning and progress messages +#--------------------------------------------------------------------------- +WARN_NO_PARAMDOC = YES + +#--------------------------------------------------------------------------- +# Configuration options related to the input files +#--------------------------------------------------------------------------- +INPUT = /home/thu/projects/neml2/README.md \ + /home/thu/projects/neml2/src \ + /home/thu/projects/neml2/include \ + /home/thu/projects/neml2/doc/content \ + /home/thu/projects/neml2/doc/content +FILE_PATTERNS = *.cxx \ + *.h \ + *.md +RECURSIVE = YES +IMAGE_PATH = /home/thu/projects/neml2/doc/content/asset +EXCLUDE_SYMBOLS = neml2::*internal neml2::*details +USE_MDFILE_AS_MAINPAGE = /home/thu/projects/neml2/README.md + +#--------------------------------------------------------------------------- +# Configuration options related to the alphabetical class index +#--------------------------------------------------------------------------- + +#--------------------------------------------------------------------------- +# Configuration options related to the preprocessor +#--------------------------------------------------------------------------- +MACRO_EXPANSION = YES + +#--------------------------------------------------------------------------- +# Configuration options related to the dot tool +#--------------------------------------------------------------------------- +HAVE_DOT = YES +COLLABORATION_GRAPH = NO +INCLUDE_GRAPH = NO +INCLUDED_BY_GRAPH = NO + +#--------------------------------------------------------------------------- +# Configuration options related to the HTML output +#--------------------------------------------------------------------------- +GENERATE_HTML = NO + +#--------------------------------------------------------------------------- +# Configuration options related to the LaTeX output +#--------------------------------------------------------------------------- +GENERATE_LATEX = NO +#--------------------------------------------------------------------------- +# Configuration options related to the HTML output +#--------------------------------------------------------------------------- +GENERATE_HTML = YES +DISABLE_INDEX = NO +FULL_SIDEBAR = NO +HTML_OUTPUT = html +HTML_EXTRA_STYLESHEET = /home/thu/projects/neml2/_deps/doxygen-awesome-css-src/doxygen-awesome.css\ + /home/thu/projects/neml2/_deps/doxygen-awesome-css-src/doxygen-awesome-sidebar-only.css +HTML_COLORSTYLE = LIGHT +GENERATE_TREEVIEW = YES +USE_MATHJAX = YES +MATHJAX_VERSION = MathJax_3 +MATHJAX_FORMAT = HTML-CSS +MATHJAX_RELPATH = https://cdn.jsdelivr.net/npm/mathjax@3 +MATHJAX_EXTENSIONS = ams physics boldsymbol +WARN_LOGFILE = /home/thu/projects/neml2/doc/doxygen.html.log diff --git a/doc/DoxyfileLaTeX.sh b/doc/DoxyfileLaTeX.sh new file mode 100644 index 0000000000..acbfd4bb49 --- /dev/null +++ b/doc/DoxyfileLaTeX.sh @@ -0,0 +1,85 @@ +# Doxyfile 1.9.8 + +#--------------------------------------------------------------------------- +# Project related configuration options +#--------------------------------------------------------------------------- +DOXYFILE_ENCODING = UTF-8 +PROJECT_NAME = NEML2 +PROJECT_NUMBER = 1.4.0 +OUTPUT_DIRECTORY = build +TOC_INCLUDE_HEADINGS = 3 + +#--------------------------------------------------------------------------- +# Build related configuration options +#--------------------------------------------------------------------------- +EXTRACT_ALL = YES +CASE_SENSE_NAMES = YES +HIDE_SCOPE_NAMES = YES +SHOW_USED_FILES = NO +SHOW_FILES = NO +SHOW_NAMESPACES = NO +LAYOUT_FILE = /home/thu/projects/neml2/doc/config/DoxygenLayout.xml + +#--------------------------------------------------------------------------- +# Configuration options related to warning and progress messages +#--------------------------------------------------------------------------- +WARN_NO_PARAMDOC = YES + +#--------------------------------------------------------------------------- +# Configuration options related to the input files +#--------------------------------------------------------------------------- +INPUT = /home/thu/projects/neml2/README.md \ + /home/thu/projects/neml2/src \ + /home/thu/projects/neml2/include \ + /home/thu/projects/neml2/doc/content \ + /home/thu/projects/neml2/doc/content +FILE_PATTERNS = *.cxx \ + *.h \ + *.md +RECURSIVE = YES +IMAGE_PATH = /home/thu/projects/neml2/doc/content/asset +EXCLUDE_SYMBOLS = neml2::*internal neml2::*details +USE_MDFILE_AS_MAINPAGE = /home/thu/projects/neml2/README.md + +#--------------------------------------------------------------------------- +# Configuration options related to the alphabetical class index +#--------------------------------------------------------------------------- + +#--------------------------------------------------------------------------- +# Configuration options related to the preprocessor +#--------------------------------------------------------------------------- +MACRO_EXPANSION = YES + +#--------------------------------------------------------------------------- +# Configuration options related to the dot tool +#--------------------------------------------------------------------------- +HAVE_DOT = YES +COLLABORATION_GRAPH = NO +INCLUDE_GRAPH = NO +INCLUDED_BY_GRAPH = NO + +#--------------------------------------------------------------------------- +# Configuration options related to the HTML output +#--------------------------------------------------------------------------- +GENERATE_HTML = NO + +#--------------------------------------------------------------------------- +# Configuration options related to the LaTeX output +#--------------------------------------------------------------------------- +GENERATE_LATEX = NO +#--------------------------------------------------------------------------- +# Configuration options related to the LaTeX output +#--------------------------------------------------------------------------- +GENERATE_LATEX = YES +LATEX_CMD_NAME = pdflatex +EXTRA_PACKAGES = {amsmath},{lmodern},{physics} +LATEX_HEADER = /home/thu/projects/neml2/doc/config/ANLReportHeader.tex +LATEX_FOOTER = /home/thu/projects/neml2/doc/config/ANLReportFooter.tex +LATEX_EXTRA_STYLESHEET = /home/thu/projects/neml2/doc/config/ANLReportExtra.sty +LATEX_HIDE_INDICES = YES +WARN_LOGFILE = /home/thu/projects/neml2/doc/doxygen.latex.log + +#--------------------------------------------------------------------------- +# Configuration options related to the dot tool +#--------------------------------------------------------------------------- +MAX_DOT_GRAPH_DEPTH = 2 diff --git a/doc/DoxyfilePython.sh b/doc/DoxyfilePython.sh new file mode 100644 index 0000000000..fde92892d1 --- /dev/null +++ b/doc/DoxyfilePython.sh @@ -0,0 +1,94 @@ +# Doxyfile 1.9.8 + +#--------------------------------------------------------------------------- +# Project related configuration options +#--------------------------------------------------------------------------- +DOXYFILE_ENCODING = UTF-8 +PROJECT_NAME = NEML2 +PROJECT_NUMBER = 1.4.0 +OUTPUT_DIRECTORY = build +TOC_INCLUDE_HEADINGS = 3 + +#--------------------------------------------------------------------------- +# Build related configuration options +#--------------------------------------------------------------------------- +EXTRACT_ALL = YES +CASE_SENSE_NAMES = YES +HIDE_SCOPE_NAMES = YES +SHOW_USED_FILES = NO +SHOW_FILES = NO +SHOW_NAMESPACES = NO +LAYOUT_FILE = /home/thu/projects/neml2/doc/config/DoxygenLayout.xml + +#--------------------------------------------------------------------------- +# Configuration options related to warning and progress messages +#--------------------------------------------------------------------------- +WARN_NO_PARAMDOC = YES + +#--------------------------------------------------------------------------- +# Configuration options related to the input files +#--------------------------------------------------------------------------- +INPUT = /home/thu/projects/neml2/README.md \ + /home/thu/projects/neml2/src \ + /home/thu/projects/neml2/include \ + /home/thu/projects/neml2/doc/content \ + /home/thu/projects/neml2/doc/content +FILE_PATTERNS = *.cxx \ + *.h \ + *.md +RECURSIVE = YES +IMAGE_PATH = /home/thu/projects/neml2/doc/content/asset +EXCLUDE_SYMBOLS = neml2::*internal neml2::*details +USE_MDFILE_AS_MAINPAGE = /home/thu/projects/neml2/README.md + +#--------------------------------------------------------------------------- +# Configuration options related to the alphabetical class index +#--------------------------------------------------------------------------- + +#--------------------------------------------------------------------------- +# Configuration options related to the preprocessor +#--------------------------------------------------------------------------- +MACRO_EXPANSION = YES + +#--------------------------------------------------------------------------- +# Configuration options related to the dot tool +#--------------------------------------------------------------------------- +HAVE_DOT = YES +COLLABORATION_GRAPH = NO +INCLUDE_GRAPH = NO +INCLUDED_BY_GRAPH = NO + +#--------------------------------------------------------------------------- +# Configuration options related to the HTML output +#--------------------------------------------------------------------------- +GENERATE_HTML = NO + +#--------------------------------------------------------------------------- +# Configuration options related to the LaTeX output +#--------------------------------------------------------------------------- +GENERATE_LATEX = NO +#--------------------------------------------------------------------------- +# Configuration options related to the HTML output +#--------------------------------------------------------------------------- +GENERATE_HTML = YES +DISABLE_INDEX = NO +FULL_SIDEBAR = NO +HTML_OUTPUT = html +HTML_EXTRA_STYLESHEET = /home/thu/projects/neml2/_deps/doxygen-awesome-css-src/doxygen-awesome.css\ + /home/thu/projects/neml2/_deps/doxygen-awesome-css-src/doxygen-awesome-sidebar-only.css +HTML_COLORSTYLE = LIGHT +GENERATE_TREEVIEW = YES +USE_MATHJAX = YES +MATHJAX_VERSION = MathJax_3 +MATHJAX_FORMAT = HTML-CSS +MATHJAX_RELPATH = https://cdn.jsdelivr.net/npm/mathjax@3 +MATHJAX_EXTENSIONS = ams physics boldsymbol +WARN_LOGFILE = /home/thu/projects/neml2/doc/doxygen.html.log +#--------------------------------------------------------------------------- +# Configuration options related to the HTML output for Python bindings +#--------------------------------------------------------------------------- +HTML_OUTPUT = html/python +FILE_PATTERNS = *.pyi +EXTENSION_MAPPING = pyi=Python +WARN_LOGFILE = /home/thu/projects/neml2/doc/doxygen.python.log +LAYOUT_FILE = /home/thu/projects/neml2/doc/config/DoxygenLayoutPython.xml diff --git a/src/neml2/CMakeLists.txt b/src/neml2/CMakeLists.txt index 4ce83e15f2..f80365d590 100644 --- a/src/neml2/CMakeLists.txt +++ b/src/neml2/CMakeLists.txt @@ -29,7 +29,7 @@ configure_file( target_compile_options(neml2 PRIVATE -Wall -Wextra -pedantic -Werror) # Group source files together if UNITY build is requested -register_unity_group(neml2 "NEML2" .) +register_unity_group(neml2 .) # NEML2 headers file(GLOB_RECURSE _NEML2_HEADERS ${NEML2_SOURCE_DIR}/include/*.h) From d1f8fe7cd382077351661f1a22bcf4580a1a8c32 Mon Sep 17 00:00:00 2001 From: Gary Hu Date: Fri, 17 May 2024 15:40:34 -0500 Subject: [PATCH 29/55] Remove doxyfile --- doc/DoxyfileHTML.sh | 86 --------------------------------------- doc/DoxyfileLaTeX.sh | 85 -------------------------------------- doc/DoxyfilePython.sh | 94 ------------------------------------------- 3 files changed, 265 deletions(-) delete mode 100644 doc/DoxyfileHTML.sh delete mode 100644 doc/DoxyfileLaTeX.sh delete mode 100644 doc/DoxyfilePython.sh diff --git a/doc/DoxyfileHTML.sh b/doc/DoxyfileHTML.sh deleted file mode 100644 index 35e340094e..0000000000 --- a/doc/DoxyfileHTML.sh +++ /dev/null @@ -1,86 +0,0 @@ -# Doxyfile 1.9.8 - -#--------------------------------------------------------------------------- -# Project related configuration options -#--------------------------------------------------------------------------- -DOXYFILE_ENCODING = UTF-8 -PROJECT_NAME = NEML2 -PROJECT_NUMBER = 1.4.0 -OUTPUT_DIRECTORY = build -TOC_INCLUDE_HEADINGS = 3 - -#--------------------------------------------------------------------------- -# Build related configuration options -#--------------------------------------------------------------------------- -EXTRACT_ALL = YES -CASE_SENSE_NAMES = YES -HIDE_SCOPE_NAMES = YES -SHOW_USED_FILES = NO -SHOW_FILES = NO -SHOW_NAMESPACES = NO -LAYOUT_FILE = /home/thu/projects/neml2/doc/config/DoxygenLayout.xml - -#--------------------------------------------------------------------------- -# Configuration options related to warning and progress messages -#--------------------------------------------------------------------------- -WARN_NO_PARAMDOC = YES - -#--------------------------------------------------------------------------- -# Configuration options related to the input files -#--------------------------------------------------------------------------- -INPUT = /home/thu/projects/neml2/README.md \ - /home/thu/projects/neml2/src \ - /home/thu/projects/neml2/include \ - /home/thu/projects/neml2/doc/content \ - /home/thu/projects/neml2/doc/content -FILE_PATTERNS = *.cxx \ - *.h \ - *.md -RECURSIVE = YES -IMAGE_PATH = /home/thu/projects/neml2/doc/content/asset -EXCLUDE_SYMBOLS = neml2::*internal neml2::*details -USE_MDFILE_AS_MAINPAGE = /home/thu/projects/neml2/README.md - -#--------------------------------------------------------------------------- -# Configuration options related to the alphabetical class index -#--------------------------------------------------------------------------- - -#--------------------------------------------------------------------------- -# Configuration options related to the preprocessor -#--------------------------------------------------------------------------- -MACRO_EXPANSION = YES - -#--------------------------------------------------------------------------- -# Configuration options related to the dot tool -#--------------------------------------------------------------------------- -HAVE_DOT = YES -COLLABORATION_GRAPH = NO -INCLUDE_GRAPH = NO -INCLUDED_BY_GRAPH = NO - -#--------------------------------------------------------------------------- -# Configuration options related to the HTML output -#--------------------------------------------------------------------------- -GENERATE_HTML = NO - -#--------------------------------------------------------------------------- -# Configuration options related to the LaTeX output -#--------------------------------------------------------------------------- -GENERATE_LATEX = NO -#--------------------------------------------------------------------------- -# Configuration options related to the HTML output -#--------------------------------------------------------------------------- -GENERATE_HTML = YES -DISABLE_INDEX = NO -FULL_SIDEBAR = NO -HTML_OUTPUT = html -HTML_EXTRA_STYLESHEET = /home/thu/projects/neml2/_deps/doxygen-awesome-css-src/doxygen-awesome.css\ - /home/thu/projects/neml2/_deps/doxygen-awesome-css-src/doxygen-awesome-sidebar-only.css -HTML_COLORSTYLE = LIGHT -GENERATE_TREEVIEW = YES -USE_MATHJAX = YES -MATHJAX_VERSION = MathJax_3 -MATHJAX_FORMAT = HTML-CSS -MATHJAX_RELPATH = https://cdn.jsdelivr.net/npm/mathjax@3 -MATHJAX_EXTENSIONS = ams physics boldsymbol -WARN_LOGFILE = /home/thu/projects/neml2/doc/doxygen.html.log diff --git a/doc/DoxyfileLaTeX.sh b/doc/DoxyfileLaTeX.sh deleted file mode 100644 index acbfd4bb49..0000000000 --- a/doc/DoxyfileLaTeX.sh +++ /dev/null @@ -1,85 +0,0 @@ -# Doxyfile 1.9.8 - -#--------------------------------------------------------------------------- -# Project related configuration options -#--------------------------------------------------------------------------- -DOXYFILE_ENCODING = UTF-8 -PROJECT_NAME = NEML2 -PROJECT_NUMBER = 1.4.0 -OUTPUT_DIRECTORY = build -TOC_INCLUDE_HEADINGS = 3 - -#--------------------------------------------------------------------------- -# Build related configuration options -#--------------------------------------------------------------------------- -EXTRACT_ALL = YES -CASE_SENSE_NAMES = YES -HIDE_SCOPE_NAMES = YES -SHOW_USED_FILES = NO -SHOW_FILES = NO -SHOW_NAMESPACES = NO -LAYOUT_FILE = /home/thu/projects/neml2/doc/config/DoxygenLayout.xml - -#--------------------------------------------------------------------------- -# Configuration options related to warning and progress messages -#--------------------------------------------------------------------------- -WARN_NO_PARAMDOC = YES - -#--------------------------------------------------------------------------- -# Configuration options related to the input files -#--------------------------------------------------------------------------- -INPUT = /home/thu/projects/neml2/README.md \ - /home/thu/projects/neml2/src \ - /home/thu/projects/neml2/include \ - /home/thu/projects/neml2/doc/content \ - /home/thu/projects/neml2/doc/content -FILE_PATTERNS = *.cxx \ - *.h \ - *.md -RECURSIVE = YES -IMAGE_PATH = /home/thu/projects/neml2/doc/content/asset -EXCLUDE_SYMBOLS = neml2::*internal neml2::*details -USE_MDFILE_AS_MAINPAGE = /home/thu/projects/neml2/README.md - -#--------------------------------------------------------------------------- -# Configuration options related to the alphabetical class index -#--------------------------------------------------------------------------- - -#--------------------------------------------------------------------------- -# Configuration options related to the preprocessor -#--------------------------------------------------------------------------- -MACRO_EXPANSION = YES - -#--------------------------------------------------------------------------- -# Configuration options related to the dot tool -#--------------------------------------------------------------------------- -HAVE_DOT = YES -COLLABORATION_GRAPH = NO -INCLUDE_GRAPH = NO -INCLUDED_BY_GRAPH = NO - -#--------------------------------------------------------------------------- -# Configuration options related to the HTML output -#--------------------------------------------------------------------------- -GENERATE_HTML = NO - -#--------------------------------------------------------------------------- -# Configuration options related to the LaTeX output -#--------------------------------------------------------------------------- -GENERATE_LATEX = NO -#--------------------------------------------------------------------------- -# Configuration options related to the LaTeX output -#--------------------------------------------------------------------------- -GENERATE_LATEX = YES -LATEX_CMD_NAME = pdflatex -EXTRA_PACKAGES = {amsmath},{lmodern},{physics} -LATEX_HEADER = /home/thu/projects/neml2/doc/config/ANLReportHeader.tex -LATEX_FOOTER = /home/thu/projects/neml2/doc/config/ANLReportFooter.tex -LATEX_EXTRA_STYLESHEET = /home/thu/projects/neml2/doc/config/ANLReportExtra.sty -LATEX_HIDE_INDICES = YES -WARN_LOGFILE = /home/thu/projects/neml2/doc/doxygen.latex.log - -#--------------------------------------------------------------------------- -# Configuration options related to the dot tool -#--------------------------------------------------------------------------- -MAX_DOT_GRAPH_DEPTH = 2 diff --git a/doc/DoxyfilePython.sh b/doc/DoxyfilePython.sh deleted file mode 100644 index fde92892d1..0000000000 --- a/doc/DoxyfilePython.sh +++ /dev/null @@ -1,94 +0,0 @@ -# Doxyfile 1.9.8 - -#--------------------------------------------------------------------------- -# Project related configuration options -#--------------------------------------------------------------------------- -DOXYFILE_ENCODING = UTF-8 -PROJECT_NAME = NEML2 -PROJECT_NUMBER = 1.4.0 -OUTPUT_DIRECTORY = build -TOC_INCLUDE_HEADINGS = 3 - -#--------------------------------------------------------------------------- -# Build related configuration options -#--------------------------------------------------------------------------- -EXTRACT_ALL = YES -CASE_SENSE_NAMES = YES -HIDE_SCOPE_NAMES = YES -SHOW_USED_FILES = NO -SHOW_FILES = NO -SHOW_NAMESPACES = NO -LAYOUT_FILE = /home/thu/projects/neml2/doc/config/DoxygenLayout.xml - -#--------------------------------------------------------------------------- -# Configuration options related to warning and progress messages -#--------------------------------------------------------------------------- -WARN_NO_PARAMDOC = YES - -#--------------------------------------------------------------------------- -# Configuration options related to the input files -#--------------------------------------------------------------------------- -INPUT = /home/thu/projects/neml2/README.md \ - /home/thu/projects/neml2/src \ - /home/thu/projects/neml2/include \ - /home/thu/projects/neml2/doc/content \ - /home/thu/projects/neml2/doc/content -FILE_PATTERNS = *.cxx \ - *.h \ - *.md -RECURSIVE = YES -IMAGE_PATH = /home/thu/projects/neml2/doc/content/asset -EXCLUDE_SYMBOLS = neml2::*internal neml2::*details -USE_MDFILE_AS_MAINPAGE = /home/thu/projects/neml2/README.md - -#--------------------------------------------------------------------------- -# Configuration options related to the alphabetical class index -#--------------------------------------------------------------------------- - -#--------------------------------------------------------------------------- -# Configuration options related to the preprocessor -#--------------------------------------------------------------------------- -MACRO_EXPANSION = YES - -#--------------------------------------------------------------------------- -# Configuration options related to the dot tool -#--------------------------------------------------------------------------- -HAVE_DOT = YES -COLLABORATION_GRAPH = NO -INCLUDE_GRAPH = NO -INCLUDED_BY_GRAPH = NO - -#--------------------------------------------------------------------------- -# Configuration options related to the HTML output -#--------------------------------------------------------------------------- -GENERATE_HTML = NO - -#--------------------------------------------------------------------------- -# Configuration options related to the LaTeX output -#--------------------------------------------------------------------------- -GENERATE_LATEX = NO -#--------------------------------------------------------------------------- -# Configuration options related to the HTML output -#--------------------------------------------------------------------------- -GENERATE_HTML = YES -DISABLE_INDEX = NO -FULL_SIDEBAR = NO -HTML_OUTPUT = html -HTML_EXTRA_STYLESHEET = /home/thu/projects/neml2/_deps/doxygen-awesome-css-src/doxygen-awesome.css\ - /home/thu/projects/neml2/_deps/doxygen-awesome-css-src/doxygen-awesome-sidebar-only.css -HTML_COLORSTYLE = LIGHT -GENERATE_TREEVIEW = YES -USE_MATHJAX = YES -MATHJAX_VERSION = MathJax_3 -MATHJAX_FORMAT = HTML-CSS -MATHJAX_RELPATH = https://cdn.jsdelivr.net/npm/mathjax@3 -MATHJAX_EXTENSIONS = ams physics boldsymbol -WARN_LOGFILE = /home/thu/projects/neml2/doc/doxygen.html.log -#--------------------------------------------------------------------------- -# Configuration options related to the HTML output for Python bindings -#--------------------------------------------------------------------------- -HTML_OUTPUT = html/python -FILE_PATTERNS = *.pyi -EXTENSION_MAPPING = pyi=Python -WARN_LOGFILE = /home/thu/projects/neml2/doc/doxygen.python.log -LAYOUT_FILE = /home/thu/projects/neml2/doc/config/DoxygenLayoutPython.xml From 9b2c59ed8ae07e3ff181cbc049b39afa4597157b Mon Sep 17 00:00:00 2001 From: Gary Hu Date: Fri, 17 May 2024 15:42:03 -0500 Subject: [PATCH 30/55] Guard policies --- CMakeLists.txt | 17 ++++++++++++++--- 1 file changed, 14 insertions(+), 3 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 1344761299..cabf0b3688 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -7,9 +7,20 @@ project(NEML2 VERSION 1.4.0 LANGUAGES CXX) # ---------------------------------------------------------------------------- # Policy # ---------------------------------------------------------------------------- -cmake_policy(SET CMP0094 NEW) # FindPython should return the first matching Python -cmake_policy(SET CMP0135 NEW) # Suppress the warning related to the new policy on fetch content's timestamp -cmake_policy(SET CMP0148 NEW) # Suppress the warning related to the new policy on FindPythonXXX +# FindPython should return the first matching Python +if(POLICY CMP0094) + cmake_policy(SET CMP0094 NEW) +endif() + +# Suppress the warning related to the new policy on fetch content's timestamp +if(POLICY CMP0135) + cmake_policy(SET CMP0135 NEW) +endif() + +# Suppress the warning related to the new policy on FindPythonXXX +if(POLICY CMP0148) + cmake_policy(SET CMP0148 NEW) +endif() # ---------------------------------------------------------------------------- # Project-level settings, options, and flags From 81a6d262cfe6e6c8d3f6af6f7b60017ad6ce69fd Mon Sep 17 00:00:00 2001 From: Gary Hu Date: Fri, 17 May 2024 15:50:27 -0500 Subject: [PATCH 31/55] Fix profiler name conflict; Install PyTorch for documentation --- .github/workflows/build_docs.yml | 2 ++ tests/profiler/CMakeLists.txt | 12 ++++++------ 2 files changed, 8 insertions(+), 6 deletions(-) diff --git a/.github/workflows/build_docs.yml b/.github/workflows/build_docs.yml index 9ce01857f9..8de3d3a45b 100644 --- a/.github/workflows/build_docs.yml +++ b/.github/workflows/build_docs.yml @@ -31,6 +31,8 @@ jobs: python-version: "3.8" - run: sudo apt install graphviz - run: pip install -r requirements.txt + - name: Install PyTorch + run: pip install torch==2.2.2 - run: | cmake \ -DCMAKE_BUILD_TYPE=Release \ diff --git a/tests/profiler/CMakeLists.txt b/tests/profiler/CMakeLists.txt index 6ac56e820b..4d438bdf8b 100644 --- a/tests/profiler/CMakeLists.txt +++ b/tests/profiler/CMakeLists.txt @@ -4,17 +4,17 @@ FetchContent_Populate(gperftools) add_subdirectory(${gperftools_SOURCE_DIR} ${gperftools_BINARY_DIR} EXCLUDE_FROM_ALL) file(GLOB_RECURSE srcs *.cxx) -add_executable(profiler ${srcs}) -register_unity_group(profiler .) -target_compile_options(profiler PRIVATE -Wall -Wextra -pedantic -Werror) -target_link_options(profiler PRIVATE "-Wl,-no-as-needed") -target_link_libraries(profiler PRIVATE testutils profiler) +add_executable(neml2_profiler ${srcs}) +register_unity_group(neml2_profiler .) +target_compile_options(neml2_profiler PRIVATE -Wall -Wextra -pedantic -Werror) +target_link_options(neml2_profiler PRIVATE "-Wl,-no-as-needed") +target_link_libraries(neml2_profiler PRIVATE testutils profiler) if(NOT ${NEML2_BINARY_DIR} STREQUAL ${NEML2_SOURCE_DIR}) file(CREATE_LINK ${NEML2_SOURCE_DIR}/tests/profiler ${NEML2_BINARY_DIR}/tests/profiler SYMBOLIC) endif() -install(TARGETS profiler) +install(TARGETS neml2_profiler) install(DIRECTORY . TYPE BIN FILES_MATCHING From b0543338d1f6e30efd93fc0a5a0e416f966ab0ec Mon Sep 17 00:00:00 2001 From: Gary Hu Date: Fri, 17 May 2024 16:01:55 -0500 Subject: [PATCH 32/55] Flatten python API namespace --- .github/workflows/build_docs.yml | 3 ++- cmake-variants.yaml | 1 + doc/CMakeLists.txt | 1 + scripts/fixup_pystub.py | 37 ++++++++++++++++++++++++++++++++ 4 files changed, 41 insertions(+), 1 deletion(-) create mode 100755 scripts/fixup_pystub.py diff --git a/.github/workflows/build_docs.yml b/.github/workflows/build_docs.yml index 8de3d3a45b..f38c29e89b 100644 --- a/.github/workflows/build_docs.yml +++ b/.github/workflows/build_docs.yml @@ -42,7 +42,8 @@ jobs: -DNEML2_DOC=ON \ . - run: cmake --build . --target html -j 2 -- - - run: cat doc/doxygen.log + - run: cat doc/doxygen.html.log + - run: cat doc/doxygen.python.log - name: Preview GitHub Pages if: ${{ github.event_name == 'pull_request' }} uses: rossjrw/pr-preview-action@v1 diff --git a/cmake-variants.yaml b/cmake-variants.yaml index 5d37682090..f16ebad3e3 100644 --- a/cmake-variants.yaml +++ b/cmake-variants.yaml @@ -32,6 +32,7 @@ libneml2: long: Build for production release buildType: RelWithDebInfo settings: + CMAKE_UNITY_BUILD: ON NEML2_TESTS: OFF NEML2_PYBIND: ON NEML2_DOC: ON diff --git a/doc/CMakeLists.txt b/doc/CMakeLists.txt index 681919e707..6737e297b0 100644 --- a/doc/CMakeLists.txt +++ b/doc/CMakeLists.txt @@ -44,6 +44,7 @@ add_custom_target(syntax-python COMMAND ${CMAKE_COMMAND} -E make_directory ${NEML2_BINARY_DIR}/doc/content/python COMMAND PYTHONPATH=. pybind11-stubgen -o ${NEML2_BINARY_DIR}/doc/content/python neml2 COMMAND PYTHONPATH=. pybind11-stubgen -o ${NEML2_BINARY_DIR}/doc/content/python neml2.math + COMMAND ${Python_EXECUTABLE} ${NEML2_SOURCE_DIR}/scripts/fixup_pystub.py ${NEML2_BINARY_DIR}/doc/content/python/neml2 VERBATIM ) diff --git a/scripts/fixup_pystub.py b/scripts/fixup_pystub.py new file mode 100755 index 0000000000..a0c7a405f6 --- /dev/null +++ b/scripts/fixup_pystub.py @@ -0,0 +1,37 @@ +#! /usr/bin/env python + +# Copyright 2023, UChicago Argonne, LLC +# All Rights Reserved +# Software Name: NEML2 -- the New Engineering material Model Library, version 2 +# By: Argonne National Laboratory +# OPEN SOURCE LICENSE (MIT) +# +# Permission is hereby granted, free of charge, to any person obtaining a copy +# of this software and associated documentation files (the "Software"), to deal +# in the Software without restriction, including without limitation the rights +# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +# copies of the Software, and to permit persons to whom the Software is +# furnished to do so, subject to the following conditions: +# +# The above copyright notice and this permission notice shall be included in +# all copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +# THE SOFTWARE. + +from pathlib import Path +import sys + +if __name__ == "__main__": + root = Path(sys.argv[1]) + for stub in root.glob("*.pyi"): + with open(stub) as f: + fixed = f.read().replace("neml2.", "") + + with open(stub, "w") as f: + f.write(fixed) From e1255e78488c44b4cf521e0b2577340d28ea99bf Mon Sep 17 00:00:00 2001 From: Gary Hu Date: Mon, 20 May 2024 11:06:18 -0500 Subject: [PATCH 33/55] Check python deps --- doc/CMakeLists.txt | 5 ++ requirements.txt | 2 +- scripts/check_python_dep.py | 105 ++++++++++++++++++++++++++++ tests/python/CMakeLists.txt | 12 ++++ tests/{ => python}/requirements.txt | 0 5 files changed, 123 insertions(+), 1 deletion(-) create mode 100755 scripts/check_python_dep.py rename tests/{ => python}/requirements.txt (100%) diff --git a/doc/CMakeLists.txt b/doc/CMakeLists.txt index 6737e297b0..1ad5a97ce5 100644 --- a/doc/CMakeLists.txt +++ b/doc/CMakeLists.txt @@ -4,7 +4,12 @@ FetchContent_MakeAvailable(doxygen) set(DOXYGEN_EXECUTABLE ${doxygen_SOURCE_DIR}/bin/doxygen) FetchContent_MakeAvailable(doxygen-awesome-css) + find_package(Python COMPONENTS Interpreter) +execute_process( + COMMAND_ERROR_IS_FATAL ANY + COMMAND ${Python_EXECUTABLE} ${NEML2_SOURCE_DIR}/scripts/check_python_dep.py ${CMAKE_CURRENT_SOURCE_DIR}/requirements.txt +) # ---------------------------------------------------------------------------- # Macro for generating and configuring Doxyfile diff --git a/requirements.txt b/requirements.txt index 6b9fb504cd..4aa6def01a 100644 --- a/requirements.txt +++ b/requirements.txt @@ -3,4 +3,4 @@ # -r doc/requirements.txt -r scripts/requirements.txt --r tests/requirements.txt +-r tests/python/requirements.txt diff --git a/scripts/check_python_dep.py b/scripts/check_python_dep.py new file mode 100755 index 0000000000..9a55983825 --- /dev/null +++ b/scripts/check_python_dep.py @@ -0,0 +1,105 @@ +#! /usr/bin/env python + +# Copyright 2023, UChicago Argonne, LLC +# All Rights Reserved +# Software Name: NEML2 -- the New Engineering material Model Library, version 2 +# By: Argonne National Laboratory +# OPEN SOURCE LICENSE (MIT) +# +# Permission is hereby granted, free of charge, to any person obtaining a copy +# of this software and associated documentation files (the "Software"), to deal +# in the Software without restriction, including without limitation the rights +# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +# copies of the Software, and to permit persons to whom the Software is +# furnished to do so, subject to the following conditions: +# +# The above copyright notice and this permission notice shall be included in +# all copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +# THE SOFTWARE. + +import sys +import importlib.metadata +from pathlib import Path + + +def show_missing_reqs(missing_reqs): + print("-" * 79) + print("There are missing Python package dependencies:") + for missing_req in missing_reqs: + print(" {}".format(missing_req)) + print("They can be installed using `pip install -r requirements.txt`.") + print("-" * 79) + + +try: + from packaging.requirements import Requirement +except: + show_missing_reqs(["packaging"]) + + +def get_reqs(path): + """ + Recursively parse the requirements.txt to gather all dependencies + """ + reqs = [] + with open(path, "r") as f: + for req in f: + req = req.strip() + if req.startswith("#"): + continue + if req.startswith("-r"): + reqfile = req.split(" ")[1] + reqs += get_reqs(path.parent / reqfile) + else: + reqs.append(Requirement(req)) + return reqs + + +def _yield_missing_reqs(req: Requirement, current_extra: str = ""): + if req.marker and not req.marker.evaluate({"extra": current_extra}): + return + + try: + version = importlib.metadata.distribution(req.name).version + except importlib.metadata.PackageNotFoundError: # req not installed + yield req + else: + if req.specifier.contains(version): + for child_req in ( + importlib.metadata.metadata(req.name).get_all("Requires-Dist") or [] + ): + child_req_obj = Requirement(child_req) + + need_check, ext = False, None + for extra in req.extras: + if child_req_obj.marker and child_req_obj.marker.evaluate( + {"extra": extra} + ): + need_check = True + ext = extra + break + + if need_check: # check for extra reqs + yield from _yield_missing_reqs(child_req_obj, ext) + + else: # main version not match + yield req + + +if __name__ == "__main__": + reqfile = Path(sys.argv[1]) + + missing_reqs = [] + for req in get_reqs(reqfile): + missing_reqs += [missing_req for missing_req in _yield_missing_reqs(req)] + + if missing_reqs: + show_missing_reqs(missing_reqs) + exit(1) diff --git a/tests/python/CMakeLists.txt b/tests/python/CMakeLists.txt index bf7f1bc72b..80d3e56635 100644 --- a/tests/python/CMakeLists.txt +++ b/tests/python/CMakeLists.txt @@ -1,3 +1,15 @@ +# ---------------------------------------------------------------------------- +# Dependencies and 3rd party packages +# ---------------------------------------------------------------------------- +find_package(Python COMPONENTS Interpreter) +execute_process( + COMMAND_ERROR_IS_FATAL ANY + COMMAND ${Python_EXECUTABLE} ${NEML2_SOURCE_DIR}/scripts/check_python_dep.py ${CMAKE_CURRENT_SOURCE_DIR}/requirements.txt +) + +# ---------------------------------------------------------------------------- +# Install test resources +# ---------------------------------------------------------------------------- if(NOT ${NEML2_BINARY_DIR} STREQUAL ${NEML2_SOURCE_DIR}) file(CREATE_LINK ${NEML2_SOURCE_DIR}/tests/python ${NEML2_BINARY_DIR}/tests/python SYMBOLIC) endif() diff --git a/tests/requirements.txt b/tests/python/requirements.txt similarity index 100% rename from tests/requirements.txt rename to tests/python/requirements.txt From 74371f6926205564307267c9b53f15c59d59dd09 Mon Sep 17 00:00:00 2001 From: Gary Hu Date: Mon, 20 May 2024 11:46:40 -0500 Subject: [PATCH 34/55] of course --- .github/workflows/python.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/python.yml b/.github/workflows/python.yml index 68a1cdc117..12c6f12e7b 100644 --- a/.github/workflows/python.yml +++ b/.github/workflows/python.yml @@ -50,7 +50,7 @@ jobs: with: python-version: "3.8" - name: Install Python dependencies - run: pip install -r tests/requirements.txt + run: pip install -r requirements.txt - name: Install PyTorch run: pip install torch==2.2.2 - run: | From 1f949669b18daf88dde1e3fe2dd3f677333e7554 Mon Sep 17 00:00:00 2001 From: Gary Hu Date: Wed, 5 Jun 2024 16:17:40 -0500 Subject: [PATCH 35/55] Switch from scikit-build-core to setuptools; Give up on building relocatable wheels; --- CMakeLists.txt | 34 +++- cmake-variants.yaml | 33 ++-- cmake/Modules/FindTorch.cmake | 1 + cmake/Modules/NEML2Dependencies.cmake | 7 + cmake/Modules/NEML2TorchConfig.cmake | 21 +-- doc/CMakeLists.txt | 1 + include/neml2/base/Parser.h | 19 +++ pyproject.toml | 41 +---- python/CMakeLists.txt | 12 +- {tests/python => python/tests}/CMakeLists.txt | 7 +- .../tests}/base/test_HITParser.i | 0 .../tests}/base/test_HITParser.py | 0 .../tests}/models/test_Model.i | 0 .../tests}/models/test_Model.py | 0 .../tests}/models/test_ParameterStore.i | 0 .../tests}/models/test_ParameterStore.py | 0 .../python => python/tests}/requirements.txt | 0 .../python => python/tests}/tensors/common.py | 0 .../tests}/tensors/test_BatchTensor.py | 0 .../tests}/tensors/test_BatchTensorBase.py | 0 .../tests}/tensors/test_FixedDimTensor.py | 0 .../tensors/test_LabeledAxisAccessor.py | 0 .../tests}/tensors/test_Scalar.py | 0 requirements.txt | 2 +- runner/CMakeLists.txt | 29 ++++ {tests => runner}/benchmark/chaboche/model.i | 0 .../benchmark/taylor_rolling_fcc/model.i | 0 .../taylor_single_orientation/model.i | 0 runner/src/main.cxx | 101 +++++++++++ scripts/check_copyright.py | 2 +- setup.py | 71 ++++++++ src/neml2/CMakeLists.txt | 20 +-- src/neml2/base/Parser.cxx | 28 +++ tests/CMakeLists.txt | 35 +--- tests/benchmark/CMakeLists.txt | 26 --- tests/benchmark/chaboche/chaboche.cxx | 46 ----- .../taylor_rolling_fcc/taylor_rolling_fcc.cxx | 48 ------ .../taylor_single_orientation.cxx | 48 ------ tests/include/utils.h | 14 +- tests/profiler/CMakeLists.txt | 28 --- tests/profiler/main.cxx | 45 ----- tests/profiler/model.i | 161 ------------------ tests/regression/CMakeLists.txt | 17 +- tests/src/CMakeLists.txt | 1 + tests/src/utils.cxx | 28 --- tests/unit/CMakeLists.txt | 17 +- tests/verification/CMakeLists.txt | 17 +- 47 files changed, 341 insertions(+), 619 deletions(-) rename {tests/python => python/tests}/CMakeLists.txt (79%) rename {tests/python => python/tests}/base/test_HITParser.i (100%) rename {tests/python => python/tests}/base/test_HITParser.py (100%) rename {tests/python => python/tests}/models/test_Model.i (100%) rename {tests/python => python/tests}/models/test_Model.py (100%) rename {tests/python => python/tests}/models/test_ParameterStore.i (100%) rename {tests/python => python/tests}/models/test_ParameterStore.py (100%) rename {tests/python => python/tests}/requirements.txt (100%) rename {tests/python => python/tests}/tensors/common.py (100%) rename {tests/python => python/tests}/tensors/test_BatchTensor.py (100%) rename {tests/python => python/tests}/tensors/test_BatchTensorBase.py (100%) rename {tests/python => python/tests}/tensors/test_FixedDimTensor.py (100%) rename {tests/python => python/tests}/tensors/test_LabeledAxisAccessor.py (100%) rename {tests/python => python/tests}/tensors/test_Scalar.py (100%) create mode 100644 runner/CMakeLists.txt rename {tests => runner}/benchmark/chaboche/model.i (100%) rename {tests => runner}/benchmark/taylor_rolling_fcc/model.i (100%) rename {tests => runner}/benchmark/taylor_single_orientation/model.i (100%) create mode 100644 runner/src/main.cxx create mode 100644 setup.py delete mode 100644 tests/benchmark/CMakeLists.txt delete mode 100644 tests/benchmark/chaboche/chaboche.cxx delete mode 100644 tests/benchmark/taylor_rolling_fcc/taylor_rolling_fcc.cxx delete mode 100644 tests/benchmark/taylor_single_orientation/taylor_single_orientation.cxx delete mode 100644 tests/profiler/CMakeLists.txt delete mode 100644 tests/profiler/main.cxx delete mode 100644 tests/profiler/model.i diff --git a/CMakeLists.txt b/CMakeLists.txt index cabf0b3688..2ecc7336f1 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -26,14 +26,13 @@ endif() # Project-level settings, options, and flags # ---------------------------------------------------------------------------- list(APPEND CMAKE_MODULE_PATH ${NEML2_SOURCE_DIR}/cmake/Modules) # CMake modules and macros -set_property(GLOBAL PROPERTY JOB_POOLS CI=6) # Ninja only: limit number of jobs in a CI build set(CMAKE_CXX_FLAGS_COVERAGE "-O0 -fprofile-arcs -ftest-coverage" CACHE STRING "Flags used by C++ compiler during coverage builds." FORCE) set(CMAKE_CXX_STANDARD 17) set(CMAKE_CXX_STANDARD_REQUIRED ON) -option(NEML2_WHEELS "Modify rpath to accomodate Python wheels" OFF) option(NEML2_TESTS "Build NEML2 tests" ON) -option(NEML2_PYBIND "Build NEML2 Python binding" OFF) -option(NEML2_DOC "Build NEML2 documentation: doxygen" OFF) +option(NEML2_RUNNER "Build a simple runner for benchmarking, profiling, debugging, etc." OFF) +option(NEML2_PYBIND "Build NEML2 Python bindings" OFF) +option(NEML2_DOC "Build NEML2 documentation (html)" OFF) # ---------------------------------------------------------------------------- # Dependencies and 3rd party packages @@ -45,9 +44,28 @@ set(PYBIND11_VERSION "2.12.0") set(HIT_VERSION "100b575af08643b5e646cac8faff6c87dd1c15a7") set(CATCH2_VERSION "3.5.4") set(GPERFTOOLS_VERSION "2.15") +set(ARGPARSE_VERSION "3.0") include(NEML2Dependencies) find_package(Torch) # This gets redirected to our FindTorch.cmake +# ---------------------------------------------------------------------------- +# PyTorch ships libraries with or without CXX11 ABI +# ---------------------------------------------------------------------------- +if(Torch_CXX11_ABI) + add_compile_definitions(_GLIBCXX_USE_CXX11_ABI=1) +else() + add_compile_definitions(_GLIBCXX_USE_CXX11_ABI=0) +endif() + +# ---------------------------------------------------------------------------- +# For relocatable install +# ---------------------------------------------------------------------------- +if(UNIX AND APPLE) + set(EXEC_DIR "@loader_path") +elseif(UNIX AND NOT APPLE) + set(EXEC_DIR "$ORIGIN") +endif() + # ---------------------------------------------------------------------------- # Build types # ---------------------------------------------------------------------------- @@ -59,13 +77,19 @@ endif() # ---------------------------------------------------------------------------- # Subdirectories # ---------------------------------------------------------------------------- -add_subdirectory(src/neml2) # base neml2 library +# base neml2 library +add_subdirectory(src/neml2) # tests if(NEML2_TESTS) add_subdirectory(tests) endif() +# runner +if(NEML2_RUNNER) + add_subdirectory(runner) +endif() + # Python bindings if(NEML2_PYBIND) add_subdirectory(python) diff --git a/cmake-variants.yaml b/cmake-variants.yaml index f16ebad3e3..19e7ec7d96 100644 --- a/cmake-variants.yaml +++ b/cmake-variants.yaml @@ -2,29 +2,31 @@ libneml2: default: development choices: development: - short: Development - long: Build for development + short: Development (C++) + long: Build for C++ development buildType: Debug settings: NEML2_TESTS: ON - NEML2_UNIT: ON - NEML2_REGRESSION: ON - NEML2_VERIFICATION: ON - NEML2_BENCHMARK: OFF - NEML2_PROFILER: OFF + NEML2_RUNNER: OFF + NEML2_PYBIND: OFF + NEML2_DOC: OFF + development-python: + short: Development (C++ and Python) + long: Build for C++ and Python binding development + buildType: Debug + settings: + NEML2_TESTS: ON + NEML2_RUNNER: OFF NEML2_PYBIND: ON - NEML2_DOC: ON + NEML2_DOC: OFF benchmarking: short: Benchmarking - long: Build benchmarks and profiler + long: Build the runner for benchmarking and profiling buildType: Release settings: - NEML2_TESTS: ON - NEML2_UNIT: OFF - NEML2_REGRESSION: OFF - NEML2_VERIFICATION: OFF - NEML2_BENCHMARK: ON - NEML2_PROFILER: ON + NEML2_TESTS: OFF + NEML2_RUNNER: ON + NEML2_RUNNER_AS_PROFILER: ON NEML2_PYBIND: OFF NEML2_DOC: OFF production: @@ -34,5 +36,6 @@ libneml2: settings: CMAKE_UNITY_BUILD: ON NEML2_TESTS: OFF + NEML2_RUNNER: OFF NEML2_PYBIND: ON NEML2_DOC: ON diff --git a/cmake/Modules/FindTorch.cmake b/cmake/Modules/FindTorch.cmake index 22b4bf0973..d15c2636ce 100644 --- a/cmake/Modules/FindTorch.cmake +++ b/cmake/Modules/FindTorch.cmake @@ -20,6 +20,7 @@ endif() # Plan B: If we are on Unix systems, we could default to downloading a CPU-only # libTorch. if(NOT DEFINED LIBTORCH_DIR) + message(STATUS "Configuring Torch") FetchContent_MakeAvailable(torch) set(LIBTORCH_DIR ${torch_SOURCE_DIR}) endif() diff --git a/cmake/Modules/NEML2Dependencies.cmake b/cmake/Modules/NEML2Dependencies.cmake index a92e9b5768..b6a445c5d8 100644 --- a/cmake/Modules/NEML2Dependencies.cmake +++ b/cmake/Modules/NEML2Dependencies.cmake @@ -55,3 +55,10 @@ FetchContent_Declare( GIT_REPOSITORY https://github.com/gperftools/gperftools.git GIT_TAG gperftools-${GPERFTOOLS_VERSION} ) + +# C++ implementation of argparse +FetchContent_Declare( + argparse + GIT_REPOSITORY https://github.com/p-ranav/argparse.git + GIT_TAG v${ARGPARSE_VERSION} +) diff --git a/cmake/Modules/NEML2TorchConfig.cmake b/cmake/Modules/NEML2TorchConfig.cmake index 320458900a..45618b607d 100644 --- a/cmake/Modules/NEML2TorchConfig.cmake +++ b/cmake/Modules/NEML2TorchConfig.cmake @@ -1,7 +1,7 @@ # libTorch comes with two flavors: one with cxx11 abi, one without. # We should be consistent with whatever is detected from the libTorch. try_compile(Torch_CXX11_ABI - ${NEML2_SOURCE_DIR}/cmake/detect_torch_cxx11_abi/build + ${NEML2_BINARY_DIR}/cmake/detect_torch_cxx11_abi/build ${NEML2_SOURCE_DIR}/cmake/detect_torch_cxx11_abi TEST CMAKE_FLAGS "-DLIBTORCH_DIR=${LIBTORCH_DIR}" @@ -15,22 +15,3 @@ file(GLOB _TORCHLIBS ${LIBTORCH_DIR}/lib/libtorch*) set(Torch_LIBRARIES ${_C10LIBS} ${_TORCHLIBS}) find_library(Torch_PYTHON_BINDING torch_python HINTS ${Torch_LINK_DIRECTORIES}) list(REMOVE_ITEM Torch_LIBRARIES ${Torch_PYTHON_BINDING}) - -# Install rpath, important for a relocatable install -if(NEML2_WHEELS) - if(UNIX) - if(APPLE) - set(CMAKE_INSTALL_RPATH "@loader_path;@loader_path/lib;@loader_path/../torch/lib;@loader_path/../../torch/lib") - else() - set(CMAKE_INSTALL_RPATH "$ORIGIN;$ORIGIN/lib;$ORIGIN/../torch/lib;$ORIGIN/../../torch/lib") - endif() - endif() -else() - if(UNIX) - if(APPLE) - set(CMAKE_INSTALL_RPATH "@loader_path;@loader_path/lib;${Torch_LINK_DIRECTORIES}") - else() - set(CMAKE_INSTALL_RPATH "$ORIGIN;$ORIGIN/lib;${Torch_LINK_DIRECTORIES}") - endif() - endif() -endif() diff --git a/doc/CMakeLists.txt b/doc/CMakeLists.txt index 1ad5a97ce5..bd91762416 100644 --- a/doc/CMakeLists.txt +++ b/doc/CMakeLists.txt @@ -1,6 +1,7 @@ # ---------------------------------------------------------------------------- # Dependencies and 3rd party packages # ---------------------------------------------------------------------------- +message(STATUS "Configuring Doxygen") FetchContent_MakeAvailable(doxygen) set(DOXYGEN_EXECUTABLE ${doxygen_SOURCE_DIR}/bin/doxygen) FetchContent_MakeAvailable(doxygen-awesome-css) diff --git a/include/neml2/base/Parser.h b/include/neml2/base/Parser.h index 94de84f69b..2031880130 100644 --- a/include/neml2/base/Parser.h +++ b/include/neml2/base/Parser.h @@ -29,6 +29,25 @@ namespace neml2 { +enum ParserType +{ + HIT, + XML, + YAML, + AUTO +}; + +/** + * @brief A convenient function to parse all options from an input file + * + * @param path Path to the input file to be parsed + * @param additional_input Additional cliargs to pass to the parser + * @param ptype Input file format + */ +void load_model(const std::string & path, + const std::string & additional_input = "", + ParserType ptype = ParserType::AUTO); + /** * @brief A parser is responsible for parsing an input file into a collection of options which * can be used by the `Factory` to manufacture corresponding objects. diff --git a/pyproject.toml b/pyproject.toml index 4a68a473eb..35b8a408ce 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,6 +1,7 @@ [build-system] -requires = ["scikit-build-core>=0.3.3"] -build-backend = "scikit_build_core.build" +# requires = ["setuptools>=42", "wheel", "cmake>=3.23"] +requires = ["setuptools>=42", "cmake>=3.23"] +build-backend = "setuptools.build_meta" [project] name = "neml2" @@ -12,39 +13,3 @@ authors = [ description = "GPU-enabled vectorized material modeling library" readme = "README.md" requires-python = ">=3.8" -dependencies = ["torch==2.2.2"] - -[tool.scikit-build] -cmake.version = ">=3.23" -cmake.build-type = "RelWithDebInfo" -wheel.expand-macos-universal-tags = true -wheel.packages = [] -wheel.install-dir = "neml2" - -[tool.scikit-build.cmake.define] -CMAKE_INSTALL_PREFIX = "install" -CMAKE_JOB_POOL_COMPILE = "CI" -CMAKE_JOB_POOL_LINK = "CI" -CMAKE_UNITY_BUILD = "ON" -NEML2_PYBIND = "ON" -NEML2_WHEELS = "ON" -NEML2_TESTS = "OFF" -NEML2_DOC = "OFF" - -[tool.cibuildwheel] -build = "cp38-* cp39-* cp310-* cp311-*" -skip = "*-win32 *-manylinux_i686 *-musllinux*" -before-build = "pip install numpy torch==2.2.2" -build-verbosity = 1 -test-requires = "pytest numpy torch==2.2.2" -test-command = "pytest {project}/tests" - -[tool.cibuildwheel.macos.environment] -# MacOS 10.15 is the lowest version that supports _full_ C++17 -MACOSX_DEPLOYMENT_TARGET = "10.15" - -[tool.cibuildwheel.linux] -repair-wheel-command = "mv {wheel} {dest_dir}/" - -[tool.cibuildwheel.macos] -repair-wheel-command = "mv {wheel} {dest_dir}/" diff --git a/python/CMakeLists.txt b/python/CMakeLists.txt index 40564b8cf2..6c0d7bde5f 100644 --- a/python/CMakeLists.txt +++ b/python/CMakeLists.txt @@ -1,3 +1,4 @@ +message(STATUS "Configuring pybind11") FetchContent_MakeAvailable(pybind11) if(NOT Torch_PYTHON_BINDING) @@ -8,6 +9,7 @@ endif() macro(add_submodule mname msrcs) pybind11_add_module(${mname} MODULE ${msrcs}) set_target_properties(${mname} PROPERTIES LIBRARY_OUTPUT_DIRECTORY neml2) + set_target_properties(${mname} PROPERTIES INSTALL_RPATH "${EXEC_DIR}/lib;${Torch_LINK_DIRECTORIES}") target_include_directories(${mname} PUBLIC ${NEML2_SOURCE_DIR}) target_link_libraries(${mname} PRIVATE pybind11::headers) target_link_libraries(${mname} PUBLIC neml2 ${Torch_PYTHON_BINDING}) @@ -32,13 +34,5 @@ install(FILES neml2/__init__.py DESTINATION .) # Tests if(NEML2_TESTS) - if(NOT ${NEML2_BINARY_DIR} STREQUAL ${NEML2_SOURCE_DIR}) - file(CREATE_LINK ${NEML2_SOURCE_DIR}/tests/python ${NEML2_BINARY_DIR}/tests/python SYMBOLIC) - endif() - - install(DIRECTORY python - TYPE BIN - FILES_MATCHING - PATTERN "*.py" - ) + add_subdirectory(tests) endif() diff --git a/tests/python/CMakeLists.txt b/python/tests/CMakeLists.txt similarity index 79% rename from tests/python/CMakeLists.txt rename to python/tests/CMakeLists.txt index 80d3e56635..d219e8c380 100644 --- a/tests/python/CMakeLists.txt +++ b/python/tests/CMakeLists.txt @@ -10,12 +10,9 @@ execute_process( # ---------------------------------------------------------------------------- # Install test resources # ---------------------------------------------------------------------------- -if(NOT ${NEML2_BINARY_DIR} STREQUAL ${NEML2_SOURCE_DIR}) - file(CREATE_LINK ${NEML2_SOURCE_DIR}/tests/python ${NEML2_BINARY_DIR}/tests/python SYMBOLIC) -endif() - install(DIRECTORY . - TYPE BIN + DESTINATION ${CMAKE_INSTALL_DATAROOTDIR}/python + COMPONENT Development FILES_MATCHING PATTERN "*.py" ) diff --git a/tests/python/base/test_HITParser.i b/python/tests/base/test_HITParser.i similarity index 100% rename from tests/python/base/test_HITParser.i rename to python/tests/base/test_HITParser.i diff --git a/tests/python/base/test_HITParser.py b/python/tests/base/test_HITParser.py similarity index 100% rename from tests/python/base/test_HITParser.py rename to python/tests/base/test_HITParser.py diff --git a/tests/python/models/test_Model.i b/python/tests/models/test_Model.i similarity index 100% rename from tests/python/models/test_Model.i rename to python/tests/models/test_Model.i diff --git a/tests/python/models/test_Model.py b/python/tests/models/test_Model.py similarity index 100% rename from tests/python/models/test_Model.py rename to python/tests/models/test_Model.py diff --git a/tests/python/models/test_ParameterStore.i b/python/tests/models/test_ParameterStore.i similarity index 100% rename from tests/python/models/test_ParameterStore.i rename to python/tests/models/test_ParameterStore.i diff --git a/tests/python/models/test_ParameterStore.py b/python/tests/models/test_ParameterStore.py similarity index 100% rename from tests/python/models/test_ParameterStore.py rename to python/tests/models/test_ParameterStore.py diff --git a/tests/python/requirements.txt b/python/tests/requirements.txt similarity index 100% rename from tests/python/requirements.txt rename to python/tests/requirements.txt diff --git a/tests/python/tensors/common.py b/python/tests/tensors/common.py similarity index 100% rename from tests/python/tensors/common.py rename to python/tests/tensors/common.py diff --git a/tests/python/tensors/test_BatchTensor.py b/python/tests/tensors/test_BatchTensor.py similarity index 100% rename from tests/python/tensors/test_BatchTensor.py rename to python/tests/tensors/test_BatchTensor.py diff --git a/tests/python/tensors/test_BatchTensorBase.py b/python/tests/tensors/test_BatchTensorBase.py similarity index 100% rename from tests/python/tensors/test_BatchTensorBase.py rename to python/tests/tensors/test_BatchTensorBase.py diff --git a/tests/python/tensors/test_FixedDimTensor.py b/python/tests/tensors/test_FixedDimTensor.py similarity index 100% rename from tests/python/tensors/test_FixedDimTensor.py rename to python/tests/tensors/test_FixedDimTensor.py diff --git a/tests/python/tensors/test_LabeledAxisAccessor.py b/python/tests/tensors/test_LabeledAxisAccessor.py similarity index 100% rename from tests/python/tensors/test_LabeledAxisAccessor.py rename to python/tests/tensors/test_LabeledAxisAccessor.py diff --git a/tests/python/tensors/test_Scalar.py b/python/tests/tensors/test_Scalar.py similarity index 100% rename from tests/python/tensors/test_Scalar.py rename to python/tests/tensors/test_Scalar.py diff --git a/requirements.txt b/requirements.txt index 4aa6def01a..442b6d9853 100644 --- a/requirements.txt +++ b/requirements.txt @@ -3,4 +3,4 @@ # -r doc/requirements.txt -r scripts/requirements.txt --r tests/python/requirements.txt +-r python/tests/requirements.txt diff --git a/runner/CMakeLists.txt b/runner/CMakeLists.txt new file mode 100644 index 0000000000..dd1236044d --- /dev/null +++ b/runner/CMakeLists.txt @@ -0,0 +1,29 @@ +include(NEML2UnityGroup) + +message(STATUS "Configuring argparse") +FetchContent_MakeAvailable(argparse) + +file(GLOB_RECURSE srcs src/*.cxx) +add_executable(runner ${srcs}) +set_target_properties(runner PROPERTIES INSTALL_RPATH "${EXEC_DIR}/../lib;${Torch_LINK_DIRECTORIES}") +register_unity_group(runner .) +target_compile_options(runner PRIVATE -Wall -Wextra -pedantic -Werror) +target_link_libraries(runner PRIVATE neml2 argparse) + +option(NEML2_RUNNER_AS_PROFILER "Additionally link the runner against gperftools profiler" OFF) + +if(NEML2_RUNNER_AS_PROFILER) + FetchContent_Populate(gperftools) + add_subdirectory(${gperftools_SOURCE_DIR} ${gperftools_BINARY_DIR} EXCLUDE_FROM_ALL) + target_link_libraries(runner PRIVATE profiler) +endif() + +install(TARGETS runner) +install(DIRECTORY benchmark + DESTINATION ${CMAKE_INSTALL_DATAROOTDIR}/benchmark + FILES_MATCHING + PATTERN "*.i" + PATTERN "*.pt" + PATTERN "*.vtest" + PATTERN "*.xml" +) diff --git a/tests/benchmark/chaboche/model.i b/runner/benchmark/chaboche/model.i similarity index 100% rename from tests/benchmark/chaboche/model.i rename to runner/benchmark/chaboche/model.i diff --git a/tests/benchmark/taylor_rolling_fcc/model.i b/runner/benchmark/taylor_rolling_fcc/model.i similarity index 100% rename from tests/benchmark/taylor_rolling_fcc/model.i rename to runner/benchmark/taylor_rolling_fcc/model.i diff --git a/tests/benchmark/taylor_single_orientation/model.i b/runner/benchmark/taylor_single_orientation/model.i similarity index 100% rename from tests/benchmark/taylor_single_orientation/model.i rename to runner/benchmark/taylor_single_orientation/model.i diff --git a/runner/src/main.cxx b/runner/src/main.cxx new file mode 100644 index 0000000000..95c227cd83 --- /dev/null +++ b/runner/src/main.cxx @@ -0,0 +1,101 @@ +// Copyright 2023, UChicago Argonne, LLC +// All Rights Reserved +// Software Name: NEML2 -- the New Engineering material Model Library, version 2 +// By: Argonne National Laboratory +// OPEN SOURCE LICENSE (MIT) +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. + +#include "neml2/base/Parser.h" +#include "neml2/base/Factory.h" +#include "neml2/drivers/Driver.h" + +#include +#include + +int +main(int argc, char * argv[]) +{ + argparse::ArgumentParser program("runner"); + + // Positional arguments + program.add_argument("input").help("path to the input file"); + program.add_argument("driver").help("name of the driver in the input file"); + program.add_argument("additional_args") + .remaining() + .help("additional command-line arguments to pass to the input file parser"); + + // Optional arguments + program.add_argument("-t", "--time") + .help("output the elapsed wall time during model evaluation") + .flag(); + + try + { + // Parse cliargs + program.parse_args(argc, argv); + const auto input = program.get("input"); + const auto drivername = program.get("driver"); + + // Remaining args + std::vector args; + try + { + args = program.get>("additional_args"); + } + catch (std::logic_error & e) + { + } + std::ostringstream args_stream; + std::copy(args.begin(), args.end(), std::ostream_iterator(args_stream, " ")); + const auto additional_cliargs = args_stream.str(); + + // Run the model + try + { + neml2::load_model(input, additional_cliargs); + auto & driver = neml2::Factory::get_object("Drivers", drivername); + + if (program["--time"] == true) + { + auto t1 = std::chrono::high_resolution_clock::now(); + driver.run(); + auto t2 = std::chrono::high_resolution_clock::now(); + auto dt = std::chrono::duration_cast(t2 - t1).count(); + std::cout << "Elapsed wall time: " << dt << " ms" << std::endl; + } + else + driver.run(); + } + catch (const std::exception & err) + { + std::cerr << "An exception was raised while running the model:\n"; + std::cerr << err.what() << std::endl; + std::exit(1); + } + } + catch (const std::exception & err) + { + std::cerr << err.what() << std::endl; + std::cerr << program; + std::exit(1); + } + + return 0; +} diff --git a/scripts/check_copyright.py b/scripts/check_copyright.py index a0430d8b83..7a9473a61e 100755 --- a/scripts/check_copyright.py +++ b/scripts/check_copyright.py @@ -131,7 +131,7 @@ def update_heading_ondemand(path, copyright, prefix, modify): additional_files = {} exclude_dirs = ["extern"] - exclude_files = ["__init__.py"] + exclude_files = ["__init__.py", "setup.py"] rootdir = Path(".") diff --git a/setup.py b/setup.py new file mode 100644 index 0000000000..375fe9e6d5 --- /dev/null +++ b/setup.py @@ -0,0 +1,71 @@ +import os +import re +import subprocess +import sys +from pathlib import Path + +from setuptools import Extension, setup +from setuptools.command.build_ext import build_ext + + +# A CMakeExtension needs a sourcedir instead of a file list. +# The name must be the _single_ output extension from the CMake build. +# If you need multiple extensions, see scikit-build. +class CMakeExtension(Extension): + def __init__(self, name: str, sourcedir: str = "") -> None: + super().__init__(name, sources=[]) + self.sourcedir = os.fspath(Path(sourcedir).resolve()) + + +class CMakeBuild(build_ext): + def build_extension(self, ext: CMakeExtension) -> None: + # Give up on windows + if self.compiler.compiler_type == "msvc": + raise RuntimeError("MSVC not supported") + + # Must be in this form due to bug in .resolve() only fixed in Python 3.10+ + ext_fullpath = Path.cwd() / self.get_ext_fullpath(ext.name) + extdir = ext_fullpath.parent.resolve() + + # Configure arguments + cmake_args = [ + "-DCMAKE_INSTALL_PREFIX={}".format(extdir), + "-DCMAKE_BUILD_TYPE=RelWithDebInfo", + "-DCMAKE_UNITY_BUILD={}".format(os.environ.get("UNITY_BUILD", "OFF")), + "-DNEML2_PYBIND=OFF", + "-DNEML2_TESTS=ON", + "-DNEML2_RUNNER=OFF", + "-DNEML2_DOC=OFF", + ] + + # Build arguments + build_args = ["-j{}".format(os.environ.get("BUILD_JOBS", "1"))] + + # Install arguments + install_args = [] + + if sys.platform.startswith("darwin"): + # Cross-compile support for macOS - respect ARCHFLAGS if set + archs = re.findall(r"-arch (\S+)", os.environ.get("ARCHFLAGS", "")) + if archs: + cmake_args += ["-DCMAKE_OSX_ARCHITECTURES={}".format(";".join(archs))] + + build_temp = Path(self.build_temp) + if not build_temp.exists(): + build_temp.mkdir(parents=True) + + subprocess.run( + ["cmake", ext.sourcedir, *cmake_args], cwd=build_temp, check=True + ) + subprocess.run( + ["cmake", "--build", ".", *build_args], cwd=build_temp, check=True + ) + subprocess.run( + ["cmake", "--install", ".", *install_args], cwd=build_temp, check=True + ) + + +setup( + ext_modules=[CMakeExtension("neml2")], + cmdclass={"build_ext": CMakeBuild}, +) diff --git a/src/neml2/CMakeLists.txt b/src/neml2/CMakeLists.txt index f80365d590..c7a73edd94 100644 --- a/src/neml2/CMakeLists.txt +++ b/src/neml2/CMakeLists.txt @@ -3,6 +3,7 @@ include(NEML2UnityGroup) # Add all the source files file(GLOB_RECURSE SRCS *.cxx) add_library(neml2 SHARED ${SRCS}) +set_target_properties(neml2 PROPERTIES INSTALL_RPATH "${EXEC_DIR};${Torch_LINK_DIRECTORIES}") # Make scalar type configurable: if(NOT NEML2_DTYPE) @@ -41,11 +42,7 @@ target_sources(neml2 FILES ${_NEML2_HEADERS} ) -install( - TARGETS neml2 - FILE_SET HEADERS - COMPONENT Development -) +install(TARGETS neml2 COMPONENT Development FILE_SET HEADERS) # torch target_include_directories(neml2 SYSTEM PUBLIC ${Torch_INCLUDE_DIRECTORIES}) @@ -53,6 +50,7 @@ target_link_directories(neml2 PUBLIC ${Torch_LINK_DIRECTORIES}) target_link_libraries(neml2 PUBLIC ${Torch_LIBRARIES}) # hit for parsing +message(STATUS "Configuring hit") FetchContent_MakeAvailable(hit) add_library(hit SHARED @@ -61,12 +59,6 @@ add_library(hit SHARED ${hit_SOURCE_DIR}/braceexpr.cc ) -if(Torch_CXX11_ABI) - target_compile_definitions(hit PUBLIC _GLIBCXX_USE_CXX11_ABI=1) -else() - target_compile_definitions(hit PUBLIC _GLIBCXX_USE_CXX11_ABI=0) -endif() - set_target_properties(hit PROPERTIES UNITY_BUILD OFF) target_include_directories(hit PUBLIC ${hit_SOURCE_DIR}/..) target_sources(hit @@ -79,9 +71,5 @@ target_sources(hit ${hit_SOURCE_DIR}/lex.h ${hit_SOURCE_DIR}/parse.h ) -install( - TARGETS hit - FILE_SET HEADERS - COMPONENT Development -) +install(TARGETS hit COMPONENT Development FILE_SET HEADERS) target_link_libraries(neml2 PUBLIC hit) diff --git a/src/neml2/base/Parser.cxx b/src/neml2/base/Parser.cxx index e1decf2cf6..cb4d1c28f7 100644 --- a/src/neml2/base/Parser.cxx +++ b/src/neml2/base/Parser.cxx @@ -21,8 +21,36 @@ // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN // THE SOFTWARE. + #include "neml2/base/Parser.h" +#include "neml2/base/Factory.h" +#include "neml2/base/HITParser.h" namespace neml2 { +void +load_model(const std::string & path, const std::string & additional_input, ParserType ptype) +{ + // We are being forward looking here + if (ptype == ParserType::AUTO) + { + if (utils::end_with(path, ".i")) + ptype = ParserType::HIT; + else if (utils::end_with(path, ".xml")) + ptype = ParserType::XML; + else if (utils::end_with(path, ".yml")) + ptype = ParserType::YAML; + } + + // but for now we only support HIT + if (ptype == ParserType::HIT) + { + HITParser parser; + + Factory::clear(); + Factory::load(parser.parse(path, additional_input)); + } + else + neml_assert(false, "Unsupported parser type"); +} } // namespace neml2 diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt index 992f6ecf48..631c7048f3 100644 --- a/tests/CMakeLists.txt +++ b/tests/CMakeLists.txt @@ -4,22 +4,12 @@ option(NEML2_UNIT "Build NEML2 unit tests" ON) option(NEML2_REGRESSION "Build NEML2 regression tests" ON) option(NEML2_VERIFICATION "Build NEML2 verification tests" ON) -option(NEML2_BENCHMARK "Build NEML2 benchmark tests" OFF) -option(NEML2_PROFILER "Build NEML2 profiler" OFF) # ---------------------------------------------------------------------------- # Dependencies and 3rd party packages # ---------------------------------------------------------------------------- -if(NEML2_UNIT OR NEML2_REGRESSION OR NEML2_VERIFICATION OR NEML2_BENCHMARK) - FetchContent_MakeAvailable(Catch2) - - # Catch2 is built as a static library, so we MUST make sure the compile definitions are compatible with ours - if(Torch_CXX11_ABI) - target_compile_definitions(Catch2 PUBLIC _GLIBCXX_USE_CXX11_ABI=1) - else() - target_compile_definitions(Catch2 PUBLIC _GLIBCXX_USE_CXX11_ABI=0) - endif() -endif() +message(STATUS "Configuring Catch2") +FetchContent_MakeAvailable(Catch2) # ---------------------------------------------------------------------------- # Subdirectories @@ -29,30 +19,15 @@ add_subdirectory(src) # Unit tests if(NEML2_UNIT) - add_subdirectory(unit build/unit) + add_subdirectory(unit) endif() # Regression tests if(NEML2_REGRESSION) - add_subdirectory(regression build/regression) + add_subdirectory(regression) endif() # Verification tests if(NEML2_VERIFICATION) - add_subdirectory(verification build/verification) -endif() - -# Benchmark tests -if(NEML2_BENCHMARK) - add_subdirectory(benchmark build/benchmark) -endif() - -# Profiler -if(NEML2_PROFILER) - add_subdirectory(profiler build/profiler) -endif() - -# pytests -if(NEML2_PYBIND) - add_subdirectory(python build/python) + add_subdirectory(verification) endif() diff --git a/tests/benchmark/CMakeLists.txt b/tests/benchmark/CMakeLists.txt deleted file mode 100644 index e01c6d4207..0000000000 --- a/tests/benchmark/CMakeLists.txt +++ /dev/null @@ -1,26 +0,0 @@ -include(NEML2UnityGroup) - -file(GLOB_RECURSE srcs *.cxx) -add_executable(benchmark ${srcs}) - -target_compile_options(benchmark PRIVATE -Wall -Wextra -pedantic -Werror) -register_unity_group(benchmark .) -target_link_libraries(benchmark PRIVATE testutils) -target_link_libraries(benchmark PRIVATE Catch2::Catch2WithMain) - -if(NOT ${NEML2_BINARY_DIR} STREQUAL ${NEML2_SOURCE_DIR}) - file(CREATE_LINK ${NEML2_SOURCE_DIR}/tests/benchmark ${NEML2_BINARY_DIR}/tests/benchmark SYMBOLIC) -endif() - -install(TARGETS benchmark) -install(DIRECTORY . - TYPE BIN - FILES_MATCHING - PATTERN "*.i" - PATTERN "*.pt" - PATTERN "*.txt" - PATTERN "*.vtest" - PATTERN "*.xml" - PATTERN "*.py" - PATTERN "*.pyi" -) diff --git a/tests/benchmark/chaboche/chaboche.cxx b/tests/benchmark/chaboche/chaboche.cxx deleted file mode 100644 index ed5f2c9938..0000000000 --- a/tests/benchmark/chaboche/chaboche.cxx +++ /dev/null @@ -1,46 +0,0 @@ -// Copyright 2023, UChicago Argonne, LLC -// All Rights Reserved -// Software Name: NEML2 -- the New Engineering material Model Library, version 2 -// By: Argonne National Laboratory -// OPEN SOURCE LICENSE (MIT) -// -// Permission is hereby granted, free of charge, to any person obtaining a copy -// of this software and associated documentation files (the "Software"), to deal -// in the Software without restriction, including without limitation the rights -// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -// copies of the Software, and to permit persons to whom the Software is -// furnished to do so, subject to the following conditions: -// -// The above copyright notice and this permission notice shall be included in -// all copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -// THE SOFTWARE. - -#include -#include - -#include "utils.h" -#include "neml2/drivers/Driver.h" - -using namespace neml2; - -TEST_CASE("Chaboche") -{ - std::vector nbatches = {1, 2, 4, 8, 16, 32, 64, 128, 256, 512, 1024, 2048, 4096, 8192}; - std::vector devices = {"cpu", "cuda"}; - - for (auto && device : devices) - for (TorchSize nbatch : nbatches) - { - const auto config = "nbatch=" + utils::stringify(nbatch) + " device=" + device; - load_model("benchmark/chaboche/model.i", config); - auto & driver = Factory::get_object("Drivers", "driver"); - BENCHMARK("{" + config + "}") { return driver.run(); }; - } -} diff --git a/tests/benchmark/taylor_rolling_fcc/taylor_rolling_fcc.cxx b/tests/benchmark/taylor_rolling_fcc/taylor_rolling_fcc.cxx deleted file mode 100644 index 2462c388fa..0000000000 --- a/tests/benchmark/taylor_rolling_fcc/taylor_rolling_fcc.cxx +++ /dev/null @@ -1,48 +0,0 @@ -// Copyright 2023, UChicago Argonne, LLC -// All Rights Reserved -// Software Name: NEML2 -- the New Engineering material Model Library, version 2 -// By: Argonne National Laboratory -// OPEN SOURCE LICENSE (MIT) -// -// Permission is hereby granted, free of charge, to any person obtaining a copy -// of this software and associated documentation files (the "Software"), to deal -// in the Software without restriction, including without limitation the rights -// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -// copies of the Software, and to permit persons to whom the Software is -// furnished to do so, subject to the following conditions: -// -// The above copyright notice and this permission notice shall be included in -// all copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -// THE SOFTWARE. - -#include -#include - -#include "utils.h" -#include "neml2/drivers/Driver.h" - -using namespace neml2; - -TEST_CASE("taylor") -{ - std::vector nbatches = {1, 2, 4, 8, 16, 32, 64, 128, 256, 512, 1024, 2048, 4096}; - std::vector devices = {"cpu", "cuda:0"}; - TorchSize ntime = 500; - - for (auto && device : devices) - for (TorchSize nbatch : nbatches) - { - const auto config = "nbatch=" + utils::stringify(nbatch) + " device=" + device + - " ntime=" + utils::stringify(ntime); - load_model("benchmark/taylor_rolling_fcc/model.i", config); - auto & driver = Factory::get_object("Drivers", "driver"); - BENCHMARK("{" + config + "}") { return driver.run(); }; - } -} diff --git a/tests/benchmark/taylor_single_orientation/taylor_single_orientation.cxx b/tests/benchmark/taylor_single_orientation/taylor_single_orientation.cxx deleted file mode 100644 index ecf36b2264..0000000000 --- a/tests/benchmark/taylor_single_orientation/taylor_single_orientation.cxx +++ /dev/null @@ -1,48 +0,0 @@ -// Copyright 2023, UChicago Argonne, LLC -// All Rights Reserved -// Software Name: NEML2 -- the New Engineering material Model Library, version 2 -// By: Argonne National Laboratory -// OPEN SOURCE LICENSE (MIT) -// -// Permission is hereby granted, free of charge, to any person obtaining a copy -// of this software and associated documentation files (the "Software"), to deal -// in the Software without restriction, including without limitation the rights -// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -// copies of the Software, and to permit persons to whom the Software is -// furnished to do so, subject to the following conditions: -// -// The above copyright notice and this permission notice shall be included in -// all copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -// THE SOFTWARE. - -#include -#include - -#include "utils.h" -#include "neml2/drivers/Driver.h" - -using namespace neml2; - -TEST_CASE("single_taylor") -{ - std::vector nbatches = {1, 2, 4, 8, 16, 32, 64, 128, 256, 512, 1024, 2048, 4096}; - std::vector devices = {"cpu", "cuda:0"}; - TorchSize ntime = 500; - - for (auto && device : devices) - for (TorchSize nbatch : nbatches) - { - const auto config = "nbatch=" + utils::stringify(nbatch) + " device=" + device + - " ntime=" + utils::stringify(ntime); - load_model("benchmark/taylor_single_orientation/model.i", config); - auto & driver = Factory::get_object("Drivers", "driver"); - BENCHMARK("{" + config + "}") { return driver.run(); }; - } -} diff --git a/tests/include/utils.h b/tests/include/utils.h index 8f553cce50..d6bc34bf9d 100644 --- a/tests/include/utils.h +++ b/tests/include/utils.h @@ -24,22 +24,10 @@ #pragma once -#include "neml2/base/HITParser.h" +#include "neml2/base/Parser.h" #include "neml2/base/Factory.h" #include "neml2/tensors/Scalar.h" -enum ParserType -{ - HIT, - XML, - YAML, - AUTO -}; - -void load_model(const std::string & path, - const std::string & additional_input = "", - ParserType ptype = ParserType::AUTO); - /** * @brief A simple finite-differencing helper to numerically approximate the derivative of the * function at the given point. diff --git a/tests/profiler/CMakeLists.txt b/tests/profiler/CMakeLists.txt deleted file mode 100644 index 4d438bdf8b..0000000000 --- a/tests/profiler/CMakeLists.txt +++ /dev/null @@ -1,28 +0,0 @@ -include(NEML2UnityGroup) - -FetchContent_Populate(gperftools) -add_subdirectory(${gperftools_SOURCE_DIR} ${gperftools_BINARY_DIR} EXCLUDE_FROM_ALL) - -file(GLOB_RECURSE srcs *.cxx) -add_executable(neml2_profiler ${srcs}) -register_unity_group(neml2_profiler .) -target_compile_options(neml2_profiler PRIVATE -Wall -Wextra -pedantic -Werror) -target_link_options(neml2_profiler PRIVATE "-Wl,-no-as-needed") -target_link_libraries(neml2_profiler PRIVATE testutils profiler) - -if(NOT ${NEML2_BINARY_DIR} STREQUAL ${NEML2_SOURCE_DIR}) - file(CREATE_LINK ${NEML2_SOURCE_DIR}/tests/profiler ${NEML2_BINARY_DIR}/tests/profiler SYMBOLIC) -endif() - -install(TARGETS neml2_profiler) -install(DIRECTORY . - TYPE BIN - FILES_MATCHING - PATTERN "*.i" - PATTERN "*.pt" - PATTERN "*.txt" - PATTERN "*.vtest" - PATTERN "*.xml" - PATTERN "*.py" - PATTERN "*.pyi" -) diff --git a/tests/profiler/main.cxx b/tests/profiler/main.cxx deleted file mode 100644 index a2befb897c..0000000000 --- a/tests/profiler/main.cxx +++ /dev/null @@ -1,45 +0,0 @@ -// Copyright 2023, UChicago Argonne, LLC -// All Rights Reserved -// Software Name: NEML2 -- the New Engineering material Model Library, version 2 -// By: Argonne National Laboratory -// OPEN SOURCE LICENSE (MIT) -// -// Permission is hereby granted, free of charge, to any person obtaining a copy -// of this software and associated documentation files (the "Software"), to deal -// in the Software without restriction, including without limitation the rights -// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -// copies of the Software, and to permit persons to whom the Software is -// furnished to do so, subject to the following conditions: -// -// The above copyright notice and this permission notice shall be included in -// all copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -// THE SOFTWARE. - -#include "utils.h" -#include "neml2/misc/error.h" -#include "neml2/drivers/Driver.h" - -using namespace neml2; - -int -main(int argc, char * argv[]) -{ - if (argc != 3) - { - std::cout << "Usage: profiler /path/to/input.i driver_name" << std::endl; - return 1; - } - - load_model(std::string(argv[1])); - auto & driver = Factory::get_object("Drivers", std::string(argv[2])); - driver.run(); - - return 0; -} diff --git a/tests/profiler/model.i b/tests/profiler/model.i deleted file mode 100644 index 6dcd2d5d05..0000000000 --- a/tests/profiler/model.i +++ /dev/null @@ -1,161 +0,0 @@ -[Tensors] - [end_time] - type = LogspaceScalar - start = -1 - end = 5 - nstep = 20 - [] - [times] - type = LinspaceScalar - start = 0 - end = end_time - nstep = 100 - [] - [exx] - type = FullScalar - batch_shape = '(20)' - value = 0.1 - [] - [eyy] - type = FullScalar - batch_shape = '(20)' - value = -0.05 - [] - [ezz] - type = FullScalar - batch_shape = '(20)' - value = -0.05 - [] - [max_strain] - type = FillSR2 - values = 'exx eyy ezz' - [] - [strains] - type = LinspaceSR2 - start = 0 - end = max_strain - nstep = 100 - [] -[] - -[Drivers] - [driver] - type = SolidMechanicsDriver - model = 'model' - times = 'times' - prescribed_strains = 'strains' - [] -[] - -[Solvers] - [newton] - type = Newton - [] -[] - -[Models] - [isoharden] - type = VoceIsotropicHardening - saturated_hardening = 100 - saturation_rate = 1.2 - [] - [kinharden] - type = SR2SumModel - from_var = 'state/internal/X1 state/internal/X2' - to_var = 'state/internal/X' - [] - [mandel_stress] - type = IsotropicMandelStress - [] - [overstress] - type = OverStress - [] - [vonmises] - type = SR2Invariant - invariant_type = 'VONMISES' - tensor = 'state/internal/O' - invariant = 'state/internal/s' - [] - [yield] - type = YieldFunction - yield_stress = 5 - isotropic_hardening = 'state/internal/k' - [] - [flow] - type = ComposedModel - models = 'overstress vonmises yield' - [] - [normality] - type = Normality - model = 'flow' - function = 'state/internal/fp' - from = 'state/internal/M state/internal/k' - to = 'state/internal/NM state/internal/Nk' - [] - [flow_rate] - type = PerzynaPlasticFlowRate - reference_stress = 100 - exponent = 2 - [] - [eprate] - type = AssociativeIsotropicPlasticHardening - [] - [X1rate] - type = ChabochePlasticHardening - back_stress = 'state/internal/X1' - C = 10000 - g = 100 - A = 1e-8 - a = 1.2 - [] - [X2rate] - type = ChabochePlasticHardening - back_stress = 'state/internal/X2' - C = 1000 - g = 9 - A = 1e-10 - a = 3.2 - [] - [Eprate] - type = AssociativePlasticFlow - [] - [Erate] - type = SR2ForceRate - force = 'E' - [] - [Eerate] - type = ElasticStrain - rate_form = true - [] - [elasticity] - type = LinearIsotropicElasticity - youngs_modulus = 1e5 - poisson_ratio = 0.3 - rate_form = true - [] - [integrate_ep] - type = ScalarBackwardEulerTimeIntegration - variable = 'internal/ep' - [] - [integrate_X1] - type = SR2BackwardEulerTimeIntegration - variable = 'internal/X1' - [] - [integrate_X2] - type = SR2BackwardEulerTimeIntegration - variable = 'internal/X2' - [] - [integrate_stress] - type = SR2BackwardEulerTimeIntegration - variable = 'S' - [] - [implicit_rate] - type = ComposedModel - models = 'isoharden kinharden mandel_stress overstress vonmises yield normality flow_rate eprate Eprate X1rate X2rate Erate Eerate elasticity integrate_stress integrate_ep integrate_X1 integrate_X2' - [] - [model] - type = ImplicitUpdate - implicit_model = 'implicit_rate' - solver = 'newton' - [] -[] diff --git a/tests/regression/CMakeLists.txt b/tests/regression/CMakeLists.txt index c5c0e3a6e0..e7f1555080 100644 --- a/tests/regression/CMakeLists.txt +++ b/tests/regression/CMakeLists.txt @@ -2,26 +2,19 @@ include(NEML2UnityGroup) file(GLOB_RECURSE srcs *.cxx) add_executable(regression_tests ${srcs}) -set_target_properties(regression_tests PROPERTIES RUNTIME_OUTPUT_DIRECTORY ${NEML2_BINARY_DIR}/tests) +set_target_properties(regression_tests PROPERTIES INSTALL_RPATH "${EXEC_DIR}/../lib;${Torch_LINK_DIRECTORIES}") target_compile_options(regression_tests PRIVATE -Wall -Wextra -pedantic -Werror) register_unity_group(regression_tests .) -target_link_libraries(regression_tests PRIVATE testutils) -target_link_libraries(regression_tests PRIVATE Catch2::Catch2WithMain) +target_link_libraries(regression_tests PRIVATE testutils Catch2::Catch2WithMain) -if(NOT ${NEML2_BINARY_DIR} STREQUAL ${NEML2_SOURCE_DIR}) - file(CREATE_LINK ${NEML2_SOURCE_DIR}/tests/regression ${NEML2_BINARY_DIR}/tests/regression SYMBOLIC) -endif() - -install(TARGETS regression_tests) +install(TARGETS regression_tests COMPONENT Development) install(DIRECTORY . - TYPE BIN + DESTINATION ${CMAKE_INSTALL_DATAROOTDIR}/regression + COMPONENT Development FILES_MATCHING PATTERN "*.i" PATTERN "*.pt" - PATTERN "*.txt" PATTERN "*.vtest" PATTERN "*.xml" - PATTERN "*.py" - PATTERN "*.pyi" ) diff --git a/tests/src/CMakeLists.txt b/tests/src/CMakeLists.txt index db3a178bf6..11db561501 100644 --- a/tests/src/CMakeLists.txt +++ b/tests/src/CMakeLists.txt @@ -1,5 +1,6 @@ file(GLOB_RECURSE TEST_UTILS *.cxx) add_library(testutils SHARED ${TEST_UTILS}) +set_target_properties(testutils PROPERTIES INSTALL_RPATH "${EXEC_DIR};${Torch_LINK_DIRECTORIES}") target_link_libraries(testutils PUBLIC neml2) target_include_directories(testutils PUBLIC ${NEML2_SOURCE_DIR}/tests/include) install(TARGETS testutils) diff --git a/tests/src/utils.cxx b/tests/src/utils.cxx index 86f22317c4..57ff07e21e 100644 --- a/tests/src/utils.cxx +++ b/tests/src/utils.cxx @@ -23,31 +23,3 @@ // THE SOFTWARE. #include "utils.h" - -using namespace neml2; - -void -load_model(const std::string & path, const std::string & additional_input, ParserType ptype) -{ - // We are being forward looking here - if (ptype == ParserType::AUTO) - { - if (utils::end_with(path, ".i")) - ptype = ParserType::HIT; - else if (utils::end_with(path, ".xml")) - ptype = ParserType::XML; - else if (utils::end_with(path, ".yml")) - ptype = ParserType::YAML; - } - - // but for now we only support HIT - if (ptype == ParserType::HIT) - { - Factory::clear(); - HITParser parser; - const auto all_options = parser.parse(path, additional_input); - Factory::load(all_options); - } - else - neml_assert(false, "Unsupported parser type"); -} diff --git a/tests/unit/CMakeLists.txt b/tests/unit/CMakeLists.txt index 04df62bbdf..290047680e 100644 --- a/tests/unit/CMakeLists.txt +++ b/tests/unit/CMakeLists.txt @@ -2,26 +2,19 @@ include(NEML2UnityGroup) file(GLOB_RECURSE srcs *.cxx) add_executable(unit_tests ${srcs}) -set_target_properties(unit_tests PROPERTIES RUNTIME_OUTPUT_DIRECTORY ${NEML2_BINARY_DIR}/tests) +set_target_properties(unit_tests PROPERTIES INSTALL_RPATH "${EXEC_DIR}/../lib;${Torch_LINK_DIRECTORIES}") target_compile_options(unit_tests PRIVATE -Wall -Wextra -pedantic -Werror) register_unity_group(unit_tests .) -target_link_libraries(unit_tests PRIVATE testutils) -target_link_libraries(unit_tests PRIVATE Catch2::Catch2WithMain) +target_link_libraries(unit_tests testutils Catch2::Catch2WithMain) -if(NOT ${NEML2_BINARY_DIR} STREQUAL ${NEML2_SOURCE_DIR}) - file(CREATE_LINK ${NEML2_SOURCE_DIR}/tests/unit ${NEML2_BINARY_DIR}/tests/unit SYMBOLIC) -endif() - -install(TARGETS unit_tests) +install(TARGETS unit_tests COMPONENT Development) install(DIRECTORY . - TYPE BIN + DESTINATION ${CMAKE_INSTALL_DATAROOTDIR}/unit + COMPONENT Development FILES_MATCHING PATTERN "*.i" PATTERN "*.pt" - PATTERN "*.txt" PATTERN "*.vtest" PATTERN "*.xml" - PATTERN "*.py" - PATTERN "*.pyi" ) diff --git a/tests/verification/CMakeLists.txt b/tests/verification/CMakeLists.txt index 41f4c53749..f72a8d9a32 100644 --- a/tests/verification/CMakeLists.txt +++ b/tests/verification/CMakeLists.txt @@ -2,26 +2,19 @@ include(NEML2UnityGroup) file(GLOB_RECURSE srcs *.cxx) add_executable(verification_tests ${srcs}) -set_target_properties(verification_tests PROPERTIES RUNTIME_OUTPUT_DIRECTORY ${NEML2_BINARY_DIR}/tests) +set_target_properties(verification_tests PROPERTIES INSTALL_RPATH "${EXEC_DIR}/../lib;${Torch_LINK_DIRECTORIES}") target_compile_options(verification_tests PRIVATE -Wall -Wextra -pedantic -Werror) register_unity_group(verification_tests .) -target_link_libraries(verification_tests PRIVATE testutils) -target_link_libraries(verification_tests PRIVATE Catch2::Catch2WithMain) +target_link_libraries(verification_tests PRIVATE testutils Catch2::Catch2WithMain) -if(NOT ${NEML2_BINARY_DIR} STREQUAL ${NEML2_SOURCE_DIR}) - file(CREATE_LINK ${NEML2_SOURCE_DIR}/tests/verification ${NEML2_BINARY_DIR}/tests/verification SYMBOLIC) -endif() - -install(TARGETS verification_tests) +install(TARGETS verification_tests COMPONENT Development) install(DIRECTORY . - TYPE BIN + DESTINATION ${CMAKE_INSTALL_DATAROOTDIR}/verification + COMPONENT Development FILES_MATCHING PATTERN "*.i" PATTERN "*.pt" - PATTERN "*.txt" PATTERN "*.vtest" PATTERN "*.xml" - PATTERN "*.py" - PATTERN "*.pyi" ) From 2c15ae14cfe1ccf3837c652230ee108950709de6 Mon Sep 17 00:00:00 2001 From: Gary Hu Date: Wed, 5 Jun 2024 16:42:07 -0500 Subject: [PATCH 36/55] Fix setuptools install directory --- setup.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/setup.py b/setup.py index 375fe9e6d5..9e579ee144 100644 --- a/setup.py +++ b/setup.py @@ -25,13 +25,13 @@ def build_extension(self, ext: CMakeExtension) -> None: # Must be in this form due to bug in .resolve() only fixed in Python 3.10+ ext_fullpath = Path.cwd() / self.get_ext_fullpath(ext.name) - extdir = ext_fullpath.parent.resolve() + extdir = ext_fullpath.parent.resolve() / "neml2" # Configure arguments cmake_args = [ "-DCMAKE_INSTALL_PREFIX={}".format(extdir), "-DCMAKE_BUILD_TYPE=RelWithDebInfo", - "-DCMAKE_UNITY_BUILD={}".format(os.environ.get("UNITY_BUILD", "OFF")), + "-DCMAKE_UNITY_BUILD=ON", "-DNEML2_PYBIND=OFF", "-DNEML2_TESTS=ON", "-DNEML2_RUNNER=OFF", From 26488daf559f17fa865f1839a902218ec1f4be44 Mon Sep 17 00:00:00 2001 From: Gary Hu Date: Wed, 5 Jun 2024 17:01:05 -0500 Subject: [PATCH 37/55] Fix workflows --- .github/workflows/build_docs.yml | 1 + .github/workflows/python.yml | 60 +++----------------------------- .github/workflows/tests.yml | 24 +++++-------- setup.py | 4 +-- 4 files changed, 17 insertions(+), 72 deletions(-) diff --git a/.github/workflows/build_docs.yml b/.github/workflows/build_docs.yml index f38c29e89b..55497cfe85 100644 --- a/.github/workflows/build_docs.yml +++ b/.github/workflows/build_docs.yml @@ -38,6 +38,7 @@ jobs: -DCMAKE_BUILD_TYPE=Release \ -DCMAKE_UNITY_BUILD=ON \ -DNEML2_TESTS=OFF \ + -DNEML2_RUNNER=OFF \ -DNEML2_PYBIND=ON \ -DNEML2_DOC=ON \ . diff --git a/.github/workflows/python.yml b/.github/workflows/python.yml index 12c6f12e7b..9776c20462 100644 --- a/.github/workflows/python.yml +++ b/.github/workflows/python.yml @@ -30,7 +30,7 @@ jobs: - uses: psf/black@stable with: options: "--check -v" - src: "python/neml2 tests/python" + src: "python/neml2 python/tests" build-test: name: Build and test Python bindings needs: black @@ -53,21 +53,8 @@ jobs: run: pip install -r requirements.txt - name: Install PyTorch run: pip install torch==2.2.2 - - run: | - cmake \ - -DCMAKE_BUILD_TYPE=Debug \ - -DCMAKE_UNITY_BUILD=ON \ - -DNEML2_UNIT=OFF \ - -DNEML2_REGRESSION=OFF \ - -DNEML2_VERIFICATION=OFF \ - -DNEML2_BENCHMARK=OFF \ - -DNEML2_PROFILER=OFF \ - -DNEML2_PYBIND=ON \ - -DNEML2_DOC=OFF \ - -B build \ - . - - run: cd build && make -j 2 - - run: PYTHONPATH=build/python pytest --junitxml=build/python/pytest.xml tests + - run: pip install -v . + - run: pytest --junitxml=build/python/pytest.xml python/tests - name: Publish Test Results uses: EnricoMi/publish-unit-test-result-action@v2 if: matrix.os == 'ubuntu-latest' @@ -93,45 +80,8 @@ jobs: with: name: package-sdist path: dist/*.tar.gz - wheels: - needs: build-test - strategy: - fail-fast: false - matrix: - os: [ubuntu-latest, macos-12] - runs-on: ${{ matrix.os }} - steps: - - name: Backup docker - if: matrix.os == 'ubuntu-latest' - run: sudo mv /var/lib/docker ${GITHUB_WORKSPACE}/docker - - name: Maximize build space - if: matrix.os == 'ubuntu-latest' - uses: easimon/maximize-build-space@master - with: - remove-dotnet: "true" - remove-android: "true" - remove-haskell: "true" - remove-codeql: "true" - build-mount-path: "/var/lib/docker/" - - name: Remount docker - if: matrix.os == 'ubuntu-latest' - run: sudo sh -c "mv ${GITHUB_WORKSPACE}/docker/* /var/lib/docker" - - uses: actions/checkout@v4 - with: - submodules: recursive - - uses: jwlawson/actions-setup-cmake@v2 - with: - cmake-version: "3.23" - - name: Build wheels - uses: pypa/cibuildwheel@v2.16.5 - - name: Upload wheels - if: github.event_name == 'release' && github.event.action == 'published' - uses: actions/upload-artifact@v4 - with: - name: package-wheels-${{ matrix.os }}-${{ strategy.job-index }} - path: ./wheelhouse/*.whl PyPI: - needs: [sdist, wheels] + needs: sdist environment: pypi permissions: id-token: write @@ -140,7 +90,7 @@ jobs: steps: - uses: actions/download-artifact@v4 with: - pattern: package-* + pattern: package-sdist path: dist merge-multiple: true - uses: pypa/gh-action-pypi-publish@release/v1 diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml index 4289fe90b4..28629694e4 100644 --- a/.github/workflows/tests.yml +++ b/.github/workflows/tests.yml @@ -68,11 +68,8 @@ jobs: cmake \ -DCMAKE_BUILD_TYPE=${{ matrix.btype }} \ -DCMAKE_UNITY_BUILD=${{ matrix.unity }} \ - -DNEML2_UNIT=ON \ - -DNEML2_REGRESSION=ON \ - -DNEML2_VERIFICATION=ON \ - -DNEML2_BENCHMARK=OFF \ - -DNEML2_PROFILER=OFF \ + -DNEML2_TESTS=ON \ + -DNEML2_RUNNER=OFF \ -DNEML2_PYBIND=OFF \ -DNEML2_DOC=OFF \ . @@ -117,11 +114,9 @@ jobs: cmake \ -DCMAKE_BUILD_TYPE=${{ matrix.btype }} \ -DCMAKE_UNITY_BUILD=${{ matrix.unity }} \ - -DNEML2_UNIT=ON \ - -DNEML2_REGRESSION=ON \ - -DNEML2_VERIFICATION=ON \ - -DNEML2_BENCHMARK=ON \ - -DNEML2_PROFILER=ON \ + -DNEML2_TESTS=ON \ + -DNEML2_RUNNER=ON \ + -DNEML2_RUNNER_AS_PROFILER=ON \ -DNEML2_PYBIND=OFF \ -DNEML2_DOC=OFF \ . @@ -147,11 +142,9 @@ jobs: cmake \ -DCMAKE_BUILD_TYPE=Coverage \ -DCMAKE_UNITY_BUILD=OFF \ - -DNEML2_UNIT=ON \ - -DNEML2_REGRESSION=ON \ - -DNEML2_VERIFICATION=ON \ - -DNEML2_BENCHMARK=OFF \ - -DNEML2_PROFILER=OFF \ + -DNEML2_TESTS=ON \ + -DNEML2_RUNNER=OFF \ + -DNEML2_PYBIND=OFF \ -DNEML2_DOC=OFF \ . - run: make -j 2 @@ -193,6 +186,7 @@ jobs: project(FOO)\n\ add_subdirectory(neml2)\n\ add_executable(foo main.cxx)\n\ + add_compile_definitions(_GLIBCXX_USE_CXX11_ABI=0)\n\ target_link_libraries(foo neml2)\n\ " > CMakeLists.txt - run: cat CMakeLists.txt diff --git a/setup.py b/setup.py index 9e579ee144..668026d6d6 100644 --- a/setup.py +++ b/setup.py @@ -32,8 +32,8 @@ def build_extension(self, ext: CMakeExtension) -> None: "-DCMAKE_INSTALL_PREFIX={}".format(extdir), "-DCMAKE_BUILD_TYPE=RelWithDebInfo", "-DCMAKE_UNITY_BUILD=ON", - "-DNEML2_PYBIND=OFF", - "-DNEML2_TESTS=ON", + "-DNEML2_PYBIND=ON", + "-DNEML2_TESTS=OFF", "-DNEML2_RUNNER=OFF", "-DNEML2_DOC=OFF", ] From d3f75c032c77a4daace5f48bfcf29cf56bc9d2fd Mon Sep 17 00:00:00 2001 From: Gary Hu Date: Wed, 5 Jun 2024 17:39:11 -0500 Subject: [PATCH 38/55] Fix pytest result publishing --- .github/workflows/python.yml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/.github/workflows/python.yml b/.github/workflows/python.yml index 9776c20462..e681d70ecb 100644 --- a/.github/workflows/python.yml +++ b/.github/workflows/python.yml @@ -54,18 +54,18 @@ jobs: - name: Install PyTorch run: pip install torch==2.2.2 - run: pip install -v . - - run: pytest --junitxml=build/python/pytest.xml python/tests + - run: pytest --junitxml=pytest.xml python/tests - name: Publish Test Results uses: EnricoMi/publish-unit-test-result-action@v2 if: matrix.os == 'ubuntu-latest' with: - files: build/python/pytest.xml + files: pytest.xml check_name: Python Binding Test Results (${{ matrix.os }}) - name: Publish Test Results uses: EnricoMi/publish-unit-test-result-action/composite@v2 if: matrix.os == 'macos-12' with: - files: build/python/pytest.xml + files: pytest.xml check_name: Python Binding Test Results (${{ matrix.os }}) sdist: name: Build source distribution From 04964a05b56dca1220250e74a55ee58110d8f7fb Mon Sep 17 00:00:00 2001 From: Gary Hu Date: Wed, 5 Jun 2024 21:18:42 -0500 Subject: [PATCH 39/55] Fix c++ tests and coverage --- .github/workflows/tests.yml | 6 +++--- scripts/coverage.sh | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml index 28629694e4..f2932d0e67 100644 --- a/.github/workflows/tests.yml +++ b/.github/workflows/tests.yml @@ -74,11 +74,11 @@ jobs: -DNEML2_DOC=OFF \ . - run: make -j 2 - - run: cd tests && ./unit_tests -r junit > unit_tests.xml + - run: cd tests && ./unit/unit_tests -r junit > unit_tests.xml continue-on-error: true - - run: cd tests && ./regression_tests -r junit > regression_tests.xml + - run: cd tests && ./regression/regression_tests -r junit > regression_tests.xml continue-on-error: true - - run: cd tests && ./verification_tests -r junit > verification_tests.xml + - run: cd tests && ./verification/verification_tests -r junit > verification_tests.xml continue-on-error: true - name: Publish Test Results uses: EnricoMi/publish-unit-test-result-action@v2 diff --git a/scripts/coverage.sh b/scripts/coverage.sh index fc5034288b..4cebca0739 100755 --- a/scripts/coverage.sh +++ b/scripts/coverage.sh @@ -33,7 +33,7 @@ export SRC_DIR=$ROOT/src export COVERAGE_DIR=$ROOT/coverage lcov --gcov-tool gcov --capture --initial --directory $SRC_DIR --output-file $COVERAGE_DIR/initialize.info cd tests -./unit_tests || true +./unit/unit_tests || true cd .. lcov --gcov-tool gcov --capture --ignore-errors gcov,source --directory $SRC_DIR --output-file $COVERAGE_DIR/covered.info lcov --gcov-tool gcov --add-tracefile $COVERAGE_DIR/initialize.info --add-tracefile $COVERAGE_DIR/covered.info --output-file $COVERAGE_DIR/final.info From 5af2f5d4e516b7eb459b72a220da4f2fa55acb5e Mon Sep 17 00:00:00 2001 From: Gary Hu Date: Thu, 6 Jun 2024 08:05:59 -0500 Subject: [PATCH 40/55] Fix testutils linking --- tests/src/CMakeLists.txt | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/tests/src/CMakeLists.txt b/tests/src/CMakeLists.txt index 11db561501..c0f4c13537 100644 --- a/tests/src/CMakeLists.txt +++ b/tests/src/CMakeLists.txt @@ -1,6 +1,4 @@ file(GLOB_RECURSE TEST_UTILS *.cxx) -add_library(testutils SHARED ${TEST_UTILS}) -set_target_properties(testutils PROPERTIES INSTALL_RPATH "${EXEC_DIR};${Torch_LINK_DIRECTORIES}") +add_library(testutils OBJECT ${TEST_UTILS}) target_link_libraries(testutils PUBLIC neml2) target_include_directories(testutils PUBLIC ${NEML2_SOURCE_DIR}/tests/include) -install(TARGETS testutils) From eb6245e68f1727afc69026af9eb136ebba13bee5 Mon Sep 17 00:00:00 2001 From: Gary Hu Date: Thu, 6 Jun 2024 08:28:53 -0500 Subject: [PATCH 41/55] Adapt documentation --- doc/content/dev.md | 26 ++++++------------------ doc/content/install.md | 45 ++++++++++++++++++++---------------------- 2 files changed, 27 insertions(+), 44 deletions(-) diff --git a/doc/content/dev.md b/doc/content/dev.md index 478a35e39f..9456ec2d31 100644 --- a/doc/content/dev.md +++ b/doc/content/dev.md @@ -187,13 +187,13 @@ A model unit test examines the outputs of a `Model` given a predefined set of in All input files for model unit tests should be stored inside `tests/unit/models`. Every input file with the `.i` extension will be automatically discovered and executed. To run all the model unit tests, use the following commands ``` cd tests -./unit_tests models +../build/unit/unit_tests models ``` To run a specific model unit test, use the `-c` command line option followed by the relative location of the input file, i.e. ``` cd tests -./unit_tests models -c solid_mechanics/LinearIsotropicElasticity.i +../build/unit/unit_tests models -c solid_mechanics/LinearIsotropicElasticity.i ``` ### Regression tests {#regression-tests} @@ -203,12 +203,12 @@ A model regression test runs a `Model` using a user specified driver. The result Each input file for model regression tests should be stored inside a separate folder inside `tests/regression`. Every input file with the `.i` extension will be automatically discovered and executed. To run all the model regression tests, use the `regression_tests` executable followed by the physics module, i.e. ``` cd tests -./regression_tests "solid mechanics" +../build/regression/regression_tests "solid mechanics" ``` To run a specific model regression test, use the `-c` command line option followed by the relative location of the input file, i.e. ``` cd tests -./regression_tests "solid mechanics" -c viscoplasticity/chaboche/model.i +../build/regression/regression_tests "solid mechanics" -c viscoplasticity/chaboche/model.i ``` Note that the regression test expects an option `reference` which specifies the relative location to the reference solution. @@ -219,27 +219,13 @@ The model verification test is similar to the model regression test in terms of Each input file for model verification tests should be stored inside a separate folder inside `tests/verification`. Every input file with the `.i` extension will be automatically discovered and executed. To run all the model verification tests, use the `verification_tests` executable followed by the physics module, i.e. ``` cd tests -./verification_tests "solid mechanics" +../build/verification/verification_tests "solid mechanics" ``` To run a specific model verification test, use the `-c` command line option followed by the relative location of the input file, i.e. ``` cd tests -./verification_tests "solid mechanics" -c chaboche/chaboche.i +../build/verification/verification_tests "solid mechanics" -c chaboche/chaboche.i ``` The regression test compares variables (specified using the `variables` option) against reference values (specified using the `references` option). The reference variables can be read using input objects with type `VTestTimeSeries`. -### Benchmarking {#benchmarking} - -The benchmark tests can be authored within the [Catch2 microbenchmarking framework](https://github.com/catchorg/Catch2/blob/v2.x/docs/benchmarks.md). Before any benchmarks can be executed, the clock's resolution is estimated. A few other environmental artifacts are also estimated at this point, like the cost of calling the clock function, but they almost never have any impact in the results. The user code is executed a few times to obtain an estimate of the amount of runs that should be in each sample. This also has the potential effect of bringing relevant code and data into the caches before the actual measurement starts. Finally, all the samples are collected sequentially by performing the number of runs estimated in the previous step for each sample. - -To run a benchmark test, use the `benchmark.sh` script inside the `scripts` directory with 3 positional arguments: -``` -./scripts/benchmark.sh Chaboche 5 timings -``` -The first positional argument specifies the name of the benchmark test to run. The second positional argument specifies the number of samples to repeat in each iteration. The third positional argument specifies the output directory of the benchmark results. - -The Chaboche benchmark test is repeated with different batch sizes and on different devices (in this case CPU and GPU). The final benchmark results are summarized in the following figure. - -![Chaboche benchmark results](@ref timings.png){html: width=50%, latex: width=10cm} - diff --git a/doc/content/install.md b/doc/content/install.md index 63b589bc30..3e45a8292b 100644 --- a/doc/content/install.md +++ b/doc/content/install.md @@ -31,6 +31,7 @@ If no PyTorch installation can be detected and `LIBTORCH_DIR` is not set at conf - [gperftools](https://github.com/gperftools/gperftools) for profiling. - [Doxygen](https://github.com/doxygen/doxygen) for building the documentation. - [Doxygen Awesome](https://github.com/jothepro/doxygen-awesome-css) the documentation theme. +- [argparse](https://github.com/p-ranav/argparse) for command-line argument parsing. - Python packages - pytest - pandas @@ -50,28 +51,24 @@ git checkout main Then, configure NEML2. See [build customization](#build-customization) for possible configuration options. ``` -mkdir build && cmake -B build . +cmake -B build . ``` Finally, compile NEML2. ``` -cd build && make -j N +cmake --build build -j N ``` where `N` is the number of cores to use for parallel compilation. -After the compilation is complete, optionally run the tests to make sure the compilation was successful. - -``` -make test -``` - The compiled NEML2 can be installed as a system library. ``` -make install +cmake --install build ``` +For more fine-grained control over the configure, build, and install commands, please refer to the [CMake documentation](https://cmake.org/cmake/help/latest/manual/cmake.1.html). + ## Build Customization {#build-customization} Additional configuration options can be passed via command line using the `-DOPTION` or `-DOPTION=ON` format. For example, @@ -83,21 +80,21 @@ turns on the `NEML2_DOC` option, and additional targets for building the Doxygen Commonly used configuration options are summarized below. Default options are underlined. -| Option | Values (default) | Description | -| :------------------- | :---------------------------------------------------------- | :---------------------------------------------------------------------------------------- | -| CMAKE_BUILD_TYPE | Debug, Release, MinSizeRel, RelWithDebInfo, Coverage | CMake [Reference](https://cmake.org/cmake/help/latest/variable/CMAKE_BUILD_TYPE.html) | -| CMAKE_INSTALL_PREFIX | | CMake [Reference](https://cmake.org/cmake/help/latest/variable/CMAKE_INSTALL_PREFIX.html) | -| CMAKE_UNITY_BUILD | | CMake [Reference](https://cmake.org/cmake/help/latest/variable/CMAKE_UNITY_BUILD.html) | -| NEML2_DTYPE | Float16, Float32, Float64 | Default floating point integral type used in the material models | -| NEML2_INT_DTYPE | Int8, Int16, Int32, Int64 | Default fixed point integral type used in the material models | -| NEML2_TESTS | ON, OFF | Master knob for including/excluding all tests | -| NEML2_UNIT | ON, OFF | Create the unit testing target | -| NEML2_REGRESSION | ON, OFF | Create the regression testing target | -| NEML2_VERIFICATION | ON, OFF | Create the verification testing target | -| NEML2_BENCHMARK | ON, OFF | Create the benchmark target | -| NEML2_PROFILER | ON, OFF | Create the profiler target | -| NEML2_DOC | ON, OFF | Create the documentation target | -| NEML2_PYBIND | ON, OFF | Create the Python bindings target | +| Option | Values (default) | Description | +| :----------------------- | :---------------------------------------------------------- | :---------------------------------------------------------------------------------------- | +| CMAKE_BUILD_TYPE | Debug, Release, MinSizeRel, RelWithDebInfo, Coverage | CMake [Reference](https://cmake.org/cmake/help/latest/variable/CMAKE_BUILD_TYPE.html) | +| CMAKE_INSTALL_PREFIX | | CMake [Reference](https://cmake.org/cmake/help/latest/variable/CMAKE_INSTALL_PREFIX.html) | +| CMAKE_UNITY_BUILD | | CMake [Reference](https://cmake.org/cmake/help/latest/variable/CMAKE_UNITY_BUILD.html) | +| NEML2_DTYPE | Float16, Float32, Float64 | Default floating point integral type used in the material models | +| NEML2_INT_DTYPE | Int8, Int16, Int32, Int64 | Default fixed point integral type used in the material models | +| NEML2_TESTS | ON, OFF | Master knob for including/excluding all tests | +| NEML2_UNIT | ON, OFF | Create the unit testing target | +| NEML2_REGRESSION | ON, OFF | Create the regression testing target | +| NEML2_VERIFICATION | ON, OFF | Create the verification testing target | +| NEML2_RUNNER | ON, OFF | Create a simple runner | +| NEML2_RUNNER_AS_PROFILER | ON, OFF | Make the runner a profiler by linking against gperftools | +| NEML2_DOC | ON, OFF | Create the documentation target | +| NEML2_PYBIND | ON, OFF | Create the Python bindings target | ## CMake integration {#cmake-integration} From 6cc74c69038f2a2acb5d181b3de112ab6ea8ccf1 Mon Sep 17 00:00:00 2001 From: Gary Hu Date: Thu, 6 Jun 2024 16:58:57 -0500 Subject: [PATCH 42/55] Add python installation instructions; Add more to the getting started page --- .gitignore | 1 + doc/content/dev.md | 2 +- doc/content/getting_started.md | 60 ++++++++++++++++++++++++++++++++-- doc/content/install.md | 58 +++++++++++++++++++++++++++++--- 4 files changed, 113 insertions(+), 8 deletions(-) diff --git a/.gitignore b/.gitignore index d2129948ed..1110c183a4 100644 --- a/.gitignore +++ b/.gitignore @@ -38,3 +38,4 @@ neml2.egg-info src/neml2/base/config.h install conftest.py +.env diff --git a/doc/content/dev.md b/doc/content/dev.md index 9456ec2d31..4171b34b26 100644 --- a/doc/content/dev.md +++ b/doc/content/dev.md @@ -172,7 +172,7 @@ register_NEML2_object(LinearIsotropicHardening); ``` so that an instance of the class can be created at runtime. -## Testing {#testing} +## Testing {#dev-testing} It is of paramount importance to ensure the correctness of the implementation. NEML2 offers 5 types of tests with different purposes. diff --git a/doc/content/getting_started.md b/doc/content/getting_started.md index 2130bf028f..1957baca17 100644 --- a/doc/content/getting_started.md +++ b/doc/content/getting_started.md @@ -8,7 +8,7 @@ The user interface of NEML2 is designed in such a way that no programing experie Since the input files are nothing more than text files saved on the disk, they can be used in any application that supports standard IO, easily exchanged among different devices running different operating systems, and archived for future reference. -## Input file syntax {#input-file-syntax} +### Input file syntax {#input-file-syntax} Input files use the Hierarchical Input Text (HIT) format. The syntax looks like this: ```python @@ -43,7 +43,7 @@ All NEML2 capabilities that can be defined through the input file fall under a n ``` defines a tensor named "E" under the `[Tensors]` block and a model named "elasticity" under the `[Models]` block. The [Syntax Documentation](@ref syntax-tensors) provides a complete list of objects that can be defined by an input file. The [System Documentation](@ref system-tensors) provides detailed explanation of each system. -## Special syntax +### Special syntax **Boolean**: Oftentimes the behavior of the object is preferrably controlled by a boolean flag. However, since the HIT format only allows (array of) integer, floating-point number, and string, a special syntax shall be reserved for boolean values. In NEML2 input files, a string with value "true" can be parsed into a boolean `true`, and a string with value "false" can be parsed into a boolean `false`. @@ -52,3 +52,59 @@ defines a tensor named "E" under the `[Tensors]` block and a model named "elasti **Variable name**: NEML2 material models work with named variables to assign physical meanings to different slices of a tensor (see e.g. [Tensor Labeling](@ref tensor-labeling)). A fully qualified variable name can be parsed from a string, and the delimiter "/" signifies nested sub-axes. For example, the string "forces/t" can be parsed into a variable named "t" defined on the sub-axis named "forces". **Tensor shape**: Shape of a tensor can also be parsed from a string. The string must start with "(" and end with ")". An array of comma-separated integers must be enclosed by the parentheses. For example, the string "(5,6,7)" can be parsed into a shape tuple of value `(5, 6, 7)`. Note that white spaces are not allowed between the parentheses and could lead to undefined behavior. An empty array, i.e. "()", however, is allowed and fully supported. + +## Using NEML2 as a C++ library + +The following input file defines a linear isotropic elasticity material model: + +```python +[Models] + [model] + type = LinearIsotropicElasticity + youngs_modulus = 100 + poisson_ratio = 0.3 + strain = 'forces/E' + stress = 'state/S' + [] +[] +``` + +The input file defines two parameters: Young's modulus of 100 and Poisson's ratio of 0.3. While optional, the input file also sets the variable names of strain and stress to be "forces/E" and "state/S", respectively (refer to the documentation on [tensor labeling](@ref tensor-labeling) for variable naming conventions). + +Assuming the above input file is named "input_file.i", the C++ code snippet below parses the input file and loads the material model (into the heap). + +```cpp +#include "neml2/base/Parser.h" +#include "neml2/base/Factory.h" +#include "neml2/models/Model.h" +#include "neml2/tensors/tensors.h" + +using namespace neml2; + +int main() { + load_model("input.i"); + auto & model = Factory::get_object("Models", "model"); + + // ... + + return 0; +} +``` + +Suppose we want to perform 3 material updates simultaneously, the model should be initialized using the neml2::Model::reinit method with the correct batch shape (refer to the [tensor system documentation](@ref system-tensors) for more detailed explanation on the term "batch"): + +```cpp + model.reinit({3}); +``` + +Finally, the following code constructs the 3 input strains `in` and performs 3 material updates _simultaneously_. Output stresses are stored in the tensor `out`. + +```cpp + auto in = LabeledVector::empty({3}, {model.input_axis()}); + + in.batch_index_put({0}, SR2::fill(0.1, 0.2, 0.3, -0.1, -0.1, 0.2)); + in.batch_index_put({1}, SR2::fill(0.2, 0.2, 0.1, -0.1, -0.2, -0.5)); + in.batch_index_put({2}, SR2::fill(0.3, -0.2, 0.05, -0.1, -0.3, 0.1)); + + auto out = model.value(in); +``` diff --git a/doc/content/install.md b/doc/content/install.md index 3e45a8292b..58398ee608 100644 --- a/doc/content/install.md +++ b/doc/content/install.md @@ -32,13 +32,16 @@ If no PyTorch installation can be detected and `LIBTORCH_DIR` is not set at conf - [Doxygen](https://github.com/doxygen/doxygen) for building the documentation. - [Doxygen Awesome](https://github.com/jothepro/doxygen-awesome-css) the documentation theme. - [argparse](https://github.com/p-ranav/argparse) for command-line argument parsing. +- [pybind11](https://github.com/pybind/pybind11) for building Python bindings. - Python packages - pytest - pandas - matplotlib - PyYAML -## Build, Test, and Install +## Build and install + +### C++ backend First, obtain the NEML2 source code. @@ -69,14 +72,41 @@ cmake --install build For more fine-grained control over the configure, build, and install commands, please refer to the [CMake documentation](https://cmake.org/cmake/help/latest/manual/cmake.1.html). -## Build Customization {#build-customization} + +### Python package + +NEML2 also provides an _experimental_ Python package which provides bindings for the primitive tensors and parsers for deserializing and running material models. Package source distributions are available on PyPI, but package wheels are currently not built and uploaded to PyPI. + +To install the NEML2 Python package, run the following command at the repository's root. + +``` +pip install -v . +``` + +The command installs a package named `%neml2` to the site-packages directory, and so it can be imported in Python scripts using + +```python +import neml2 +``` + +For security reasons, static analysis tools and IDEs for Python usually refuse to extract function signature, type hints, etc. from bindary extensions such as the NEML2 Python bindings. As a workaround, "stubs" can be generated a priori to make them less opaque. The NEML2 python package works well with `pybind11-stubgen` for that purpose. Stubs can be generated using the following command: + +``` +pip install pybind11-stubgen +pybind11-stubgen neml2 +``` + +Refer to the [pybind11-stubgen documentation](https://pypi.org/project/pybind11-stubgen/) for more command-line options. Most static analysis tools and IDEs can understand the stubs and therefore provide the full set of features. + + +## Build customization {#build-customization} Additional configuration options can be passed via command line using the `-DOPTION` or `-DOPTION=ON` format. For example, ``` -cmake -DNEML2_DOC=ON -B build . +cmake -DNEML2_PYBIND=ON -B build . ``` -turns on the `NEML2_DOC` option, and additional targets for building the Doxygen documentation will be created inside the Makefile. Note that this would also download additional optional dependencies that are required to build the documentation. +turns on the `NEML2_PYBIND` option, and additional targets for building the Python bindings will be created. Note that this would also download additional optional dependencies, e.g., pybind11, that are required to build the Python bindings. Commonly used configuration options are summarized below. Default options are underlined. @@ -107,7 +137,7 @@ add_executable(foo main.cxx) target_link_libraries(foo neml2) ``` -The above snippet assumes NEML2 is checked out to the directory neml2, i.e., as a git submodule. +The above snippet assumes NEML2 is checked out to the directory %neml2, i.e., as a git submodule. Alternatively, you may use CMake's `FetchContent` module to integrate NEML2 into your project: ``` @@ -121,3 +151,21 @@ FetchContent_MakeAvailable(neml2) add_executable(foo main.cxx) target_link_libraries(foo neml2) ``` + +## Testing {#testing} + +### C++ backend + +By default when `NEML2_TESTS` is set to `ON`, three test suites are built under the specified build directory: + +- `tests/unit/unit_tests`: Collection of tests to ensure individual objects are working correctly. +- `tests/regression/regression_tests`: Collection of tests to avoid regression. +- `tests/verification/verification_tests`: Collection of verification problems. + +The tests assume the working directory to be the `tests` directory relative to the repository root. For Visual Studio Code users, the [C++ TestMate](https://github.com/matepek/vscode-catch2-test-adapter) extension can be used to automatically discover and run tests. In the extension settings, the "Working Directory" variable should be modified to `${workspaceFolder}/tests`. + +### Python package + +A collection of tests are available under `python/tests` to ensure the NEML2 Python package is working correctly. For Visual Studio Code users, the [Python](https://github.com/Microsoft/vscode-python) extension can be used to automatically discover and run tests. In the extension settings, the "Pytest Enabled" variable shall be set to true. + +If the Python bindings are built (with `NEML2_PYBIND` set to `ON`) but are not installed to the site-packages directory, pytest will not be able to import the %neml2 package unless the environment variable `PYTHONPATH` is modified according to the specified build directory. For Visual Studio Code users, create a `.env` file in the repository's root and include an entry `PYTHONPATH=build/python` (assuming the build directory is `build`), and the Python extension will be able to import the NEML2 Python package. From a2fb1ec7206e764d5b100aa3574ed1d31bac8a9b Mon Sep 17 00:00:00 2001 From: Mark Messner Date: Wed, 12 Jun 2024 10:19:28 -0500 Subject: [PATCH 43/55] Added crystal plasticity docs --- doc/content/physics/solid_mechanics.md | 71 ++++++++++++++++++++++++++ 1 file changed, 71 insertions(+) diff --git a/doc/content/physics/solid_mechanics.md b/doc/content/physics/solid_mechanics.md index 7642b3601a..8cc35326b8 100644 --- a/doc/content/physics/solid_mechanics.md +++ b/doc/content/physics/solid_mechanics.md @@ -412,3 +412,74 @@ In the above example, a model named "normality" is used to compute the associati ## Crystal plasticity + +NEML2 adopts an incremental rate-form view of crystal plasticity. The fundemental kinematics derive from the rate expansion of the elastic-plastic multiplactive split: + +\f{align*} + F = F^e F^p +\f} + +where the spatial velocity gradient is then + +\f{align*} + l = \dot{F} F^{-1} = \dot{F}^e F^{e-1} + F^e \dot{F}^{p} {F}^{p-1} F^{e-1} +\f} + +The plastic deformation \f$ \bar{l}^p = \dot{F}^{p} {F}^{p-1} \f$ defines the crystal plasticity kinemtics and NEML2 assumes that the elastic stretch is small (\f$ F^e = \left(I + \varepsilon \right) R^e \f$) so that spatial velocity gradient becomes + +\f{align*} + l = \dot{\varepsilon} + \Omega^e - \Omega^e \varepsilon + \varepsilon \Omega^e + l^p + \varepsilon l^p - l^p \varepsilon +\f} + +defining \f$ l^p = R^e \bar{l}^p R^{eT} \f$ as the constitutive plastic velocity gradient rotated into the current configuration and \f$ \Omega^e = \dot{R}^e R^{eT} \f$ as the elastic spin and assuming that + +1. Terms quadratic in the elastic stretch (\f$ \varepsilon\f$) are small. +2. Terms quadratic in the rate of elastic stretch (\f$ \dot{\varepsilon} \f$) are also small. + +The first assumption is accurate for metal plasticity, the second assumption is more questionable if the material deforms at a fast strain rate. + +Define the current orientation of a crystal as the composition of its initial rotation from the crystal system to the lab frame and the elastic rotation, i.e. + +\f{align*} + Q = R^e Q_0 +\f} + +and note with this defintion we can rewrite the spin equation: + +\f{align*} + \Omega^e = \dot{R}^e R^{eT} = \dot{Q} Q_0^T Q_0 Q^T = \dot{Q} Q^T +\f} + +With this definition and the choice of kinematics above we can derive evolution equations for our fundemental constitutive quantities, the elastic stretch \f$ \varepsilon \f$ and the orientation $Q$ by splitting the spatial velocity gradient into symmetric and skew parts and rearranging the resulting equations: + +\f{align*} + \dot{\varepsilon}= d -d^p-\varepsilon w + w \varepsilon \\ + \dot{Q} = \left(w - w^p - \varepsilon d^p + d^p \varepsilon\right) Q . +\f} + +where \f$l = d + w\f$ and \f$l^p = d^p + w^p\f$ + +For most (or all) choices of crystal plasticity constitutive models we also need to define the Cauchy stress as: +\f{align*} + \sigma = C : \varepsilon +\f} + +with \f$ C \f$ a (generally) anisotropic crystal elasticity tensor rotated into the current configuration. + +The crystal plasticity examples in NEML2 integrate the elastic strain (and the constitutive internal variables) using a backward Euler integration rule and integrate the crystal orientation using either an implicit or explicit exponential rule. These integrate rules can either be coupled or decoupled, i.e. integrated together in a fully implicit manner or first integrate the strain and internal variables and then sequentially integrating the rotations. + +A full constitutive model must then define the plastic deformation $l^p$ and whatever internal variables are used in this definition. A wide variety of choices are possible, but the examples use the basic assumption of Asaro: + +\f{align*} + l^p = \sum_{i=1}^{n_{slip}} \dot{\gamma}_i Q \left(d_i \otimes n_i \right) Q^T +\f} + +where now \f$ \dot{gamma}_i \f$, the slip rate on each system, is the constitutive chioce. NEML provides a variety of options for defining these slip rates in terms of internal hardening variables and the results shear stress + +\f{align*} + \tau_i = \sigma : Q \operatorname{sym}\left(d_i \otimes n_i \right) Q^T +\f} + +Ancillary classes automatically generate lists of slip and twin systems from the crystal sytem, so the user does not need to manually provide these themselves. + +NEML2 uses *modified* Rodrigues parameters to define orientations internally. These can be converted to Euler angles, quaternions, etc. for output. \ No newline at end of file From 78a56775da0c7a90277d50bc4a548cae2ef601ce Mon Sep 17 00:00:00 2001 From: Gary Hu Date: Wed, 12 Jun 2024 14:22:42 -0500 Subject: [PATCH 44/55] Address individual comments --- README.md | 36 ++++++++++++++++++++++++---------- doc/content/getting_started.md | 24 +++++++++++++++++++++++ doc/content/install.md | 4 ++-- pyproject.toml | 1 - 4 files changed, 52 insertions(+), 13 deletions(-) diff --git a/README.md b/README.md index ff5962599e..5634c00b43 100644 --- a/README.md +++ b/README.md @@ -2,7 +2,7 @@ [![Documentation](https://github.com/reverendbedford/neml2/actions/workflows/build_docs.yml/badge.svg?branch=main)](https://reverendbedford.github.io/neml2/) [![tests](https://github.com/reverendbedford/neml2/actions/workflows/tests.yml/badge.svg?branch=main)](https://github.com/reverendbedford/neml2/actions/workflows/tests.yml) -### The New Engineering Material model Library, version 2 +## The New Engineering Material model Library, version 2 NEML2 is an offshoot of [NEML](https://github.com/Argonne-National-Laboratory/neml), an earlier material modeling code developed at Argonne National Laboratory. Like its predecessor, NEML2 provides a flexible, modular way to build material models from smaller blocks. @@ -15,16 +15,32 @@ NEML2 is provided as open source software under a MIT [license](https://raw.gith > NEML2 is _not_ a database of material models. There are many example material models in the library for testing and verification purposes. These models do not represent the response of any actual material. -### Installation +### Quick installation -Building should be as easy as cloning the repository, configuring with CMake, building with `make`, testing with `make test`, and installing with `make install`. - -By default, NEML2 will be compiled against the PyTorch package in the current environment. Finer control over various dependencies as well as other build customization is documented in the [Installation Guide](https://reverendbedford.github.io/neml2/install.html). +Building should be as easy as cloning the repository, configuring with CMake, compiling with `make`, and installing with `make install`. Refer to the [installation guide](https://reverendbedford.github.io/neml2/install.html) for more detailed instructions and finer control over various dependencies as well as other build customization. ### Features and design philosophy -- **Vectorization**: NEML2 models can be vectorized, meaning that a large _batch_ of material models can be evaluated simultaneously. The vectorized models can be evaluated on both CPU and GPU. Moreover, NEML2 provides a unified implementation and user interface, for both developers and end users, that work on all supported devices. -- **Modular and flexible material models**: NEML2 material models are modular – they are built up from smaller pieces into a complete model. NEML2 offers an extremely flexible way of composing models. Each individual model only defines the forward operator (and optionally its derivative) with a given set of inputs and outputs. When a set of models are *composed* together to form a composite model, dependencies among different models are automatically detected, registered, and resolved. The user has *complete control* over how NEML2 evaluates a set of models. -- **Extensible material models**: The library is structured so that adding a new feature to an existing material model should be as simple as possible and require as little code as possible. In line with this philosophy, the library only requires new components to provide a few partial derivatives, and NEML2 uses this information to automatically assemble the overall Jacobian using chain rule and provide the algorithmic tangent needed to integrate the model into an implicit finite element framework. Moreover, in NEML2, implementations can forgo providing these partial derivatives, and NEML2 will calculate them with automatic differentiation. -- **Friendly user interfaces**: There are two general ways of interfacing with NEML2 material models: the compiled C++ library and the Python bindings. In both interfaces, creation and archival of NEML2 models rely on input files written in the hierarchical [HIT](https://github.com/idaholab/moose/tree/master/framework/contrib/hit) format. NEML2 models created using the Pytbon bindings are fully compatible with PyTorch's automatic differentiation, meaning that NEML2 material models can seamlessly work with popular machine learning frameworks. -- **Strict quality assurance**: NEML2 is developed under a strict quality assurance program. Because the NEML2 distributions do not provide full, parameterized models for any actual materials, ensuring the quality of the library is a verification problem – testing to make sure that NEML2 is correctly implementing the mathematical models – rather than a validation problem of comparing the results of a model to an experiment. In NEML2, this verification is done with extensive unit testing. Additional regression tests are set up for each combination of material model to ensure result consistency across releases. +#### Vectorization + +NEML2 models can be vectorized, meaning that a large _batch_ of material models can be evaluated simultaneously. The vectorized models can be evaluated on both CPU and GPU. Moreover, NEML2 provides a unified implementation and user interface, for both developers and end users, that work on all supported devices. + +#### Multiphysics coupling + +NEML2 is not tied to any underlying problem physics, e.g., solid mechanics, heat transfer, fluid dynamics, electromagnetics, etc. Current modules cover thermal and mechanical material models, but the framework *can* be used to implement a wider range of constitutive models. For coupled problems, NEML2 will return the exact, coupled Jacobian entries required to achieve optimal convergence. + +#### Modularity and flexibility + +NEML2 material models are modular – they are built up from smaller pieces into a complete model. NEML2 offers an extremely flexible way of composing models. Each individual model only defines the forward operator (and optionally its derivative) with a given set of inputs and outputs. When a set of models are *composed* together to form a composite model, dependencies among different models are automatically detected, registered, and resolved. The user has *complete control* over how NEML2 evaluates a set of models. + +#### Extensibility + +The library is structured so that adding a new feature to an existing material model should be as simple as possible and require as little code as possible. In line with this philosophy, the library only requires new components to provide a few partial derivatives, and NEML2 uses this information to automatically assemble the overall Jacobian using chain rule and provide the algorithmic tangent needed to integrate the model into an implicit finite element framework. Moreover, in NEML2, implementations can forgo providing these partial derivatives, and NEML2 will calculate them with automatic differentiation. + +#### Friendly user interfaces + +There are two general ways of interfacing with NEML2 material models: the compiled C++ library and the Python bindings. In both interfaces, creation and archival of NEML2 models rely on input files written in the hierarchical [HIT](https://github.com/idaholab/moose/tree/master/framework/contrib/hit) format. NEML2 models created using the Pytbon bindings are fully compatible with PyTorch's automatic differentiation, meaning that NEML2 material models can seamlessly work with popular machine learning frameworks. + +#### Strict quality assurance + +NEML2 is developed under a strict quality assurance program. Because the NEML2 distributions do not provide full, parameterized models for any actual materials, ensuring the quality of the library is a verification problem – testing to make sure that NEML2 is correctly implementing the mathematical models – rather than a validation problem of comparing the results of a model to an experiment. In NEML2, this verification is done with extensive unit testing. Additional regression tests are set up for each combination of material model to ensure result consistency across releases. diff --git a/doc/content/getting_started.md b/doc/content/getting_started.md index 1957baca17..55d1fb1abc 100644 --- a/doc/content/getting_started.md +++ b/doc/content/getting_started.md @@ -108,3 +108,27 @@ Finally, the following code constructs the 3 input strains `in` and performs 3 m auto out = model.value(in); ``` + +## Using the NEML2 Python package + +With the NEML2 Python package, the above input file can be directly used in a Python script. The Python APIs closely ressembles the C++ APIs. For example, the above C++ source code translates to the following Python script. + +```python +import torch +import neml2 +from neml2.tensors import SR2, LabeledVector + +model = neml2.load_model("input.i", "model") + +model.reinit(3) + +x = LabeledVector.empty(3, [model.input_axis()]) + +x.batch_index_put(0, SR2.fill([0.1, 0.2, 0.3, -0.1, -0.1, 0.2])) +x.batch_index_put(1, SR2.fill([0.2, 0.2, 0.1, -0.1, -0.2, -0.5])) +x.batch_index_put(2, SR2.fill([0.3, -0.2, 0.05, -0.1, -0.3, 0.1])) + +y = model.value(x) +``` + +As is the same in the equivalent C++ source code, the above Python script parses the input file named "input.i", loads the linear elasticity model named "model", constructs 3 strain tensors, and finally performs the 3 material updates simultaneously. diff --git a/doc/content/install.md b/doc/content/install.md index 58398ee608..017fb73716 100644 --- a/doc/content/install.md +++ b/doc/content/install.md @@ -15,7 +15,7 @@ Compiling the NEML2 core library requires - [PyTorch](https://pytorch.org/get-started/locally/), version 2.2.2. Other PyTorch releases with a few minor versions around are likely to be compatible. In the PyTorch official download page, several download options are provided: conda, pip, libTorch, and source distribution. -- **Recommended** If you choose to download PyTorch using conda or pip, the NEML2 CMake script can automatically detect and use the PyTorch installation. +- **Recommended**: If you choose to download PyTorch using conda or pip, the NEML2 CMake script can automatically detect and use the PyTorch installation. - If you choose to download libTorch or build PyTorch from source, you will need to set `LIBTORCH_DIR` to be the location of libTorch when using CMake to configure NEML2. If no PyTorch installation can be detected and `LIBTORCH_DIR` is not set at configure time, the NEML2 CMake script will automatically download and use the libTorch obtained from the official website. Note, however, that this method only works on Linux and Mac systems. @@ -108,7 +108,7 @@ cmake -DNEML2_PYBIND=ON -B build . ``` turns on the `NEML2_PYBIND` option, and additional targets for building the Python bindings will be created. Note that this would also download additional optional dependencies, e.g., pybind11, that are required to build the Python bindings. -Commonly used configuration options are summarized below. Default options are underlined. +Commonly used configuration options are summarized below. Default options are underlined. | Option | Values (default) | Description | | :----------------------- | :---------------------------------------------------------- | :---------------------------------------------------------------------------------------- | diff --git a/pyproject.toml b/pyproject.toml index 35b8a408ce..163998fbcd 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,5 +1,4 @@ [build-system] -# requires = ["setuptools>=42", "wheel", "cmake>=3.23"] requires = ["setuptools>=42", "cmake>=3.23"] build-backend = "setuptools.build_meta" From 8b350c418947275f819fbe10a664471a9748535a Mon Sep 17 00:00:00 2001 From: Gary Hu Date: Wed, 12 Jun 2024 15:58:59 -0500 Subject: [PATCH 45/55] some more cpp docstring (not syntax docstring) --- doc/content/getting_started.md | 2 +- include/neml2/base/CrossRef.h | 7 ++-- include/neml2/base/DependencyResolver.h | 43 ++++++++++++++++++++++++- include/neml2/base/Factory.h | 12 +++---- include/neml2/base/NEML2Object.h | 2 ++ include/neml2/base/OptionSet.h | 6 ++-- include/neml2/base/Parser.h | 2 +- include/neml2/base/Registry.h | 4 +-- include/neml2/models/BufferStore.h | 4 +-- include/neml2/models/ParameterStore.h | 4 +-- include/neml2/models/VariableStore.h | 20 ++++++------ include/neml2/tensors/LabeledTensor.h | 2 +- include/neml2/tensors/Variable.h | 2 +- 13 files changed, 77 insertions(+), 33 deletions(-) diff --git a/doc/content/getting_started.md b/doc/content/getting_started.md index 55d1fb1abc..7757f92c0c 100644 --- a/doc/content/getting_started.md +++ b/doc/content/getting_started.md @@ -109,7 +109,7 @@ Finally, the following code constructs the 3 input strains `in` and performs 3 m auto out = model.value(in); ``` -## Using the NEML2 Python package +## Using the NEML2 Python package (experimental) With the NEML2 Python package, the above input file can be directly used in a Python script. The Python APIs closely ressembles the C++ APIs. For example, the above C++ source code translates to the following Python script. diff --git a/include/neml2/base/CrossRef.h b/include/neml2/base/CrossRef.h index 589ae53dce..e22231b333 100644 --- a/include/neml2/base/CrossRef.h +++ b/include/neml2/base/CrossRef.h @@ -55,15 +55,16 @@ class CrossRef /** * @brief Assignment operator + * + * This simply assigns the string without parsing and resolving the cross-reference */ CrossRef & operator=(const std::string & other); /** * @brief Implicit conversion operator. * - * It is assumed that the cross-referenced object has already been manufactured at this point. - * - * @return T The resolved value. + * The underlying string is parsed and used to resolve the cross-reference. It is assumed that the + * cross-referenced object has already been manufactured at this point. */ operator T() const; diff --git a/include/neml2/base/DependencyResolver.h b/include/neml2/base/DependencyResolver.h index bc2788e183..63cbb952ab 100644 --- a/include/neml2/base/DependencyResolver.h +++ b/include/neml2/base/DependencyResolver.h @@ -44,6 +44,10 @@ template class DependencyResolver { public: + /** + * Similar to @tparam ItemType but additionally contains information about its @tparam Node + * parent, i.e. the node which defines this consumed/provided item. + */ struct Item { Item(Node * const node, const ItemType & item) @@ -52,19 +56,25 @@ class DependencyResolver { } + /// Node which defines this item Node * const parent; + + /// The consumed/provided item const ItemType value; + /// Test for equality between two items bool operator==(const Item & other) const { return parent == other.parent && value == other.value; } + /// Test for inequality between two items bool operator!=(const Item & other) const { return parent != other.parent || value != other.value; } + /// An arbitrary comparator so that items can be sorted (for consistency) bool operator<(const Item & other) const { return parent != other.parent ? (parent < other.parent) : (value < other.value); @@ -73,30 +83,61 @@ class DependencyResolver DependencyResolver() = default; + /// Add a node (defining consumed/provided items) in the dependency graph void add_node(DependencyDefinition *); + /// Add an additional outbound item that the dependency graph _provides_ void add_additional_outbound_item(const ItemType & item); - /// Set a node's priority + /// Set a node's priority, useful for resolving cyclic dependency void set_priority(DependencyDefinition *, size_t); /// Resolve nodal dependency and find an evaluation order void resolve(); + /// The resolved (nodal) evaluation order following which all consumed items of the current node const std::vector & resolution() const { return _resolution; } + /** + * The item-item provider dictionary: key of the dictionary is the item of interest, and the value + * of the dictionary is the set of items that _provide_ the item of interest. + */ const std::map> & item_providers() const { return _item_provider_graph; } + + /** + * The item-item consumer dictionary: key of the dictionary is the item of interest, and the value + * of the dictionary is the set of items that _consume_ the item of interest. + */ const std::map> & item_consumers() const { return _item_consumer_graph; } + + /** + * The node-node provider dictionary: key of the dictionary is the node of interest, and the value + * of the dictionary is the set of nodes that _provide_ the item of interest. + */ const std::map> & node_providers() const { return _node_provider_graph; } + + /** + * The node-node consumer dictionary: key of the dictionary is the node of interest, and the value + * of the dictionary is the set of nodes that _consume_ the item of interest. + */ const std::map> & node_consumers() const { return _node_consumer_graph; } + /// End nodes which are not consumed by anyone else const std::set & end_nodes() const { return _end_nodes; } + + /// The items provided by the overall dependency graph, i.e., the items that are not consumed by _any_ node. const std::set & outbound_items() const { return _out_items; } + /// Start nodes which do not consume anyone else const std::set & start_nodes() const { return _start_nodes; } + + /// The items consumed by the overall dependency graph, i.e., the items that are not provided by _any_ node. const std::set & inbound_items() const { return _in_items; } + /// @returns a boolean flag controlling whether item provider should be unique bool & unique_item_provider() { return _unique_item_provider; } + + /// @returns a boolean flag controlling whether item consumer should be unique bool & unique_item_consumer() { return _unique_item_consumer; } private: diff --git a/include/neml2/base/Factory.h b/include/neml2/base/Factory.h index 7583590c40..dc2e006a2c 100644 --- a/include/neml2/base/Factory.h +++ b/include/neml2/base/Factory.h @@ -32,8 +32,8 @@ namespace neml2 { /** * The factory is responsible for: - * 1. retriving a `NEML2Object` given the object name as a `std::string` - * 2. creating a `NEML2Object` given the type of the `NEML2Object` as a `std::string`. + * 1. retriving a NEML2Object given the object name as a std::string + * 2. creating a NEML2Object given the type of the NEML2Object as a std::string. */ class Factory { @@ -41,7 +41,7 @@ class Factory /// The sequence which we use to manufacture objects. static std::vector pipeline; - /// Get the global `Factory` singleton. + /// Get the global Factory singleton. static Factory & get(); /** @@ -52,7 +52,7 @@ class Factory * - the object with the given name exists but does not have the correct type (e.g., dynamic case * fails). * - * @tparam T The type of the `NEML2Object` + * @tparam T The type of the NEML2Object * @param section The section name under which the search happens. * @param name The name of the object to retrieve. * @param additional_options Additional input options to pass to the object constructor @@ -74,7 +74,7 @@ class Factory * - the object with the given name exists but does not have the correct type (e.g., dynamic case * fails). * - * @tparam T The type of the `NEML2Object` + * @tparam T The type of the NEML2Object * @param section The section name under which the search happens. * @param name The name of the object to retrieve. * @param additional_options Additional input options to pass to the object constructor @@ -111,7 +111,7 @@ class Factory protected: /** - * @brief Manufacture a single `NEML2Object`. + * @brief Manufacture a single NEML2Object. * * @param section The section which the object to be manufactured belongs to. * @param options The options of the object. diff --git a/include/neml2/base/NEML2Object.h b/include/neml2/base/NEML2Object.h index 9286014791..c61e570835 100644 --- a/include/neml2/base/NEML2Object.h +++ b/include/neml2/base/NEML2Object.h @@ -71,9 +71,11 @@ class NEML2Object /// A readonly reference to the object's docstring const std::string & doc() const { return _options.doc(); } + /// Get a readonly pointer to the host template const T * host() const; + /// Get a writable pointer to the host template T * host(); diff --git a/include/neml2/base/OptionSet.h b/include/neml2/base/OptionSet.h index 0f9fc719d9..9b6e6c7aea 100644 --- a/include/neml2/base/OptionSet.h +++ b/include/neml2/base/OptionSet.h @@ -37,18 +37,18 @@ namespace neml2 { +///@{ /** - * Helper functions for printing scalar, vector, vector. Called from + * Helper functions for printing scalar, vector, vector of vector. Called from * OptionSet::Option::print(...). */ template void print_helper(std::ostream & os, const P *); - template void print_helper(std::ostream & os, const std::vector

*); - template void print_helper(std::ostream & os, const std::vector> *); +///@} /** * @brief A custom map-like data structure. The keys are strings, and the values can be diff --git a/include/neml2/base/Parser.h b/include/neml2/base/Parser.h index 2031880130..a4e23339e7 100644 --- a/include/neml2/base/Parser.h +++ b/include/neml2/base/Parser.h @@ -50,7 +50,7 @@ void load_model(const std::string & path, /** * @brief A parser is responsible for parsing an input file into a collection of options which - * can be used by the `Factory` to manufacture corresponding objects. + * can be used by the Factory to manufacture corresponding objects. * */ class Parser diff --git a/include/neml2/base/Registry.h b/include/neml2/base/Registry.h index 350c89621e..e41991189b 100644 --- a/include/neml2/base/Registry.h +++ b/include/neml2/base/Registry.h @@ -45,8 +45,8 @@ using BuildPtr = std::shared_ptr (*)(const OptionSet & options); * The Registry is used as a global singleton to collect information on all available NEML2Object * that can manufactured from the input file. * - * To register a concrete class to the registry, use the macro `register_NEML2_object` or - * `register_NEML2_object_alias`. Each object/class should only be registered once. + * To register a concrete class to the registry, use the macro register_NEML2_object or + * register_NEML2_object_alias. Each object/class should only be registered once. */ class Registry { diff --git a/include/neml2/models/BufferStore.h b/include/neml2/models/BufferStore.h index 22518181ba..e89880dabe 100644 --- a/include/neml2/models/BufferStore.h +++ b/include/neml2/models/BufferStore.h @@ -38,13 +38,13 @@ class BufferStore BufferStore(const OptionSet & options, NEML2Object * object); /// @returns the buffer storage - /// @{ + ///@{ const Storage & named_buffers() const { return const_cast(this)->named_buffers(); } Storage & named_buffers(); - /// }@ + ///}@ /// Get a writable reference of a buffer template & named_parameters() const { return const_cast(this)->named_parameters(); } Storage & named_parameters(); - /// }@ + ///}@ /// Get a writable reference of a parameter template Variable & get_input_variable(const VariableName & name) { @@ -68,7 +68,7 @@ class VariableStore /// @} /// Get an output variable - /// @{ + ///@{ template const Variable & get_output_variable(const VariableName & name) { @@ -87,49 +87,49 @@ class VariableStore /// @} /// Definition of the input variables - /// @{ + ///@{ LabeledAxis & input_axis() { return _input_axis; } const LabeledAxis & input_axis() const { return _input_axis; } /// @} /// Which variables this object defines as output - /// @{ + ///@{ LabeledAxis & output_axis() { return _output_axis; } const LabeledAxis & output_axis() const { return _output_axis; } /// @} /// Input variable views - /// @{ + ///@{ Storage & input_views() { return _input_views; } const Storage & input_views() const { return _input_views; } /// @} /// Output variable views - /// @{ + ///@{ Storage & output_views() { return _output_views; } const Storage & output_views() const { return _output_views; } /// @} /// Input storage - /// @{ + ///@{ LabeledVector & input_storage() { return _in; } const LabeledVector & input_storage() const { return _in; } /// @} /// Output storage - /// @{ + ///@{ LabeledVector & output_storage() { return _out; } const LabeledVector & output_storage() const { return _out; } /// @} /// Derivative storage - /// @{ + ///@{ LabeledMatrix & derivative_storage() { return _dout_din; } const LabeledMatrix & derivative_storage() const { return _dout_din; } /// @} /// Second derivative storage - /// @{ + ///@{ LabeledTensor3D & second_derivative_storage() { return _d2out_din2; } const LabeledTensor3D & second_derivative_storage() const { return _d2out_din2; } /// @} diff --git a/include/neml2/tensors/LabeledTensor.h b/include/neml2/tensors/LabeledTensor.h index 1fa223bc03..e9a4be7e7f 100644 --- a/include/neml2/tensors/LabeledTensor.h +++ b/include/neml2/tensors/LabeledTensor.h @@ -103,7 +103,7 @@ class LabeledTensor void zero_(); /// Get the underlying tensor - /// @{ + ///@{ const BatchTensor & tensor() const { return _tensor; } BatchTensor & tensor() { return _tensor; } /// @} diff --git a/include/neml2/tensors/Variable.h b/include/neml2/tensors/Variable.h index 39c359e5c5..73d41a0ca4 100644 --- a/include/neml2/tensors/Variable.h +++ b/include/neml2/tensors/Variable.h @@ -82,7 +82,7 @@ class VariableBase /// Create a wrapper representing the second derivative d2y/dx2 Derivative d(const VariableBase & x1, const VariableBase & x2); - /// @{ Accessors for storage + ///@{ Accessors for storage const LabeledVector & value_storage() const; const LabeledMatrix & derivative_storage() const; const LabeledTensor3D & second_derivative_storage() const; From c6148d2783a3483454dfaa9a1c2504b87e038795 Mon Sep 17 00:00:00 2001 From: Gary Hu Date: Wed, 12 Jun 2024 19:20:52 -0500 Subject: [PATCH 46/55] Remove Fill3DVec; Document syntax for all tensors --- .../neml2/tensors/user_tensors/Fill3DVec.h | 52 ------------------ src/neml2/models/ArrheniusParameter.cxx | 1 - .../tensors/user_tensors/EmptyBatchTensor.cxx | 7 +++ .../user_tensors/EmptyFixedDimTensor.cxx | 9 ++++ src/neml2/tensors/user_tensors/Fill3DVec.cxx | 53 ------------------- src/neml2/tensors/user_tensors/FillR2.cxx | 9 ++++ src/neml2/tensors/user_tensors/FillRot.cxx | 6 +++ src/neml2/tensors/user_tensors/FillSR2.cxx | 9 ++++ src/neml2/tensors/user_tensors/FillWR2.cxx | 4 ++ .../tensors/user_tensors/FullBatchTensor.cxx | 9 ++++ .../user_tensors/FullFixedDimTensor.cxx | 11 ++++ .../user_tensors/IdentityBatchTensor.cxx | 7 +++ .../user_tensors/LinspaceBatchTensor.cxx | 16 ++++++ .../user_tensors/LinspaceFixedDimTensor.cxx | 18 +++++++ .../user_tensors/LogspaceBatchTensor.cxx | 17 +++++- .../user_tensors/LogspaceFixedDimTensor.cxx | 20 +++++++ .../tensors/user_tensors/OnesBatchTensor.cxx | 6 +++ .../user_tensors/OnesFixedDimTensor.cxx | 8 +++ .../tensors/user_tensors/UserBatchTensor.cxx | 9 ++++ .../user_tensors/UserFixedDimTensor.cxx | 12 +++++ .../tensors/user_tensors/ZerosBatchTensor.cxx | 6 +++ .../user_tensors/ZerosFixedDimTensor.cxx | 8 +++ .../crystallography/test_CrystalGeometry.i | 23 ++++---- .../tensors/user_tensors/test_Fill3DVec.cxx | 47 ---------------- .../tensors/user_tensors/test_Fill3DVec.i | 14 ----- 25 files changed, 201 insertions(+), 180 deletions(-) delete mode 100644 include/neml2/tensors/user_tensors/Fill3DVec.h delete mode 100644 src/neml2/tensors/user_tensors/Fill3DVec.cxx delete mode 100644 tests/unit/tensors/user_tensors/test_Fill3DVec.cxx delete mode 100644 tests/unit/tensors/user_tensors/test_Fill3DVec.i diff --git a/include/neml2/tensors/user_tensors/Fill3DVec.h b/include/neml2/tensors/user_tensors/Fill3DVec.h deleted file mode 100644 index 60e37c2a1a..0000000000 --- a/include/neml2/tensors/user_tensors/Fill3DVec.h +++ /dev/null @@ -1,52 +0,0 @@ -// Copyright 2023, UChicago Argonne, LLC -// All Rights Reserved -// Software Name: NEML2 -- the New Engineering material Model Library, version 2 -// By: Argonne National Laboratory -// OPEN SOURCE LICENSE (MIT) -// -// Permission is hereby granted, free of charge, to any person obtaining a copy -// of this software and associated documentation files (the "Software"), to deal -// in the Software without restriction, including without limitation the rights -// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -// copies of the Software, and to permit persons to whom the Software is -// furnished to do so, subject to the following conditions: -// -// The above copyright notice and this permission notice shall be included in -// all copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -// THE SOFTWARE. - -#pragma once - -#include "neml2/tensors/user_tensors/UserTensor.h" - -#include "neml2/tensors/Vec.h" - -namespace neml2 -{ -/** - * @brief Create a single-batched "list" of 3D vectors - */ -class Fill3DVec : public Vec, public UserTensor -{ -public: - static OptionSet expected_options(); - - /** - * @brief Construct a new Fill3DVec object - * - * @param options The options extracted from the input file. - */ - Fill3DVec(const OptionSet & options); - -private: - /// A helper method to dispatch to the correct fill method based on the number of values. - Vec fill(const std::vector & values) const; -}; -} // namespace neml2 diff --git a/src/neml2/models/ArrheniusParameter.cxx b/src/neml2/models/ArrheniusParameter.cxx index 7d17422379..0c16fba3d1 100644 --- a/src/neml2/models/ArrheniusParameter.cxx +++ b/src/neml2/models/ArrheniusParameter.cxx @@ -32,7 +32,6 @@ OptionSet ArrheniusParameter::expected_options() { OptionSet options = NonlinearParameter::expected_options(); - options.doc() = "Define the nonlinear parameter as a function of temperature according to the " "Arrhenius law. The nonlinear parameter is therefore parametrized by the " "reference value and the activation energy."; diff --git a/src/neml2/tensors/user_tensors/EmptyBatchTensor.cxx b/src/neml2/tensors/user_tensors/EmptyBatchTensor.cxx index 5328897436..79edc70ced 100644 --- a/src/neml2/tensors/user_tensors/EmptyBatchTensor.cxx +++ b/src/neml2/tensors/user_tensors/EmptyBatchTensor.cxx @@ -32,8 +32,15 @@ OptionSet EmptyBatchTensor::expected_options() { OptionSet options = UserTensor::expected_options(); + options.doc() = "Construct an empty BatchTensor given batch and base shapes. Tensor values are " + "**undefined** after construction."; + options.set("batch_shape") = {}; + options.set("batch_shape").doc() = "Batch shape"; + options.set("base_shape") = {}; + options.set("base_shape").doc() = "Base shape"; + return options; } diff --git a/src/neml2/tensors/user_tensors/EmptyFixedDimTensor.cxx b/src/neml2/tensors/user_tensors/EmptyFixedDimTensor.cxx index 7814329a98..d5c3aace44 100644 --- a/src/neml2/tensors/user_tensors/EmptyFixedDimTensor.cxx +++ b/src/neml2/tensors/user_tensors/EmptyFixedDimTensor.cxx @@ -33,8 +33,17 @@ template OptionSet EmptyFixedDimTensor::expected_options() { + // This is the only way of getting tensor type in a static method like this... + // Trim 6 chars to remove 'neml2::' + auto tensor_type = utils::demangle(typeid(T).name()).substr(7); + OptionSet options = UserTensor::expected_options(); + options.doc() = "Construct an empty " + tensor_type + + " with given batch shape. Tensor values are **undefined** after construction."; + options.set("batch_shape") = {}; + options.set("batch_shape").doc() = "Batch shape"; + return options; } diff --git a/src/neml2/tensors/user_tensors/Fill3DVec.cxx b/src/neml2/tensors/user_tensors/Fill3DVec.cxx deleted file mode 100644 index 19da76f96a..0000000000 --- a/src/neml2/tensors/user_tensors/Fill3DVec.cxx +++ /dev/null @@ -1,53 +0,0 @@ -// Copyright 2023, UChicago Argonne, LLC -// All Rights Reserved -// Software Name: NEML2 -- the New Engineering material Model Library, version 2 -// By: Argonne National Laboratory -// OPEN SOURCE LICENSE (MIT) -// -// Permission is hereby granted, free of charge, to any person obtaining a copy -// of this software and associated documentation files (the "Software"), to deal -// in the Software without restriction, including without limitation the rights -// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -// copies of the Software, and to permit persons to whom the Software is -// furnished to do so, subject to the following conditions: -// -// The above copyright notice and this permission notice shall be included in -// all copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -// THE SOFTWARE. - -#include "neml2/tensors/user_tensors/Fill3DVec.h" - -namespace neml2 -{ -register_NEML2_object(Fill3DVec); - -OptionSet -Fill3DVec::expected_options() -{ - OptionSet options = UserTensor::expected_options(); - options.set>("values"); - return options; -} - -Fill3DVec::Fill3DVec(const OptionSet & options) - : Vec(fill(options.get>("values"))), - UserTensor(options) -{ -} - -Vec -Fill3DVec::fill(const std::vector & values) const -{ - if ((values.size() % 3) != 0) - neml_assert(false, "Number of provided values must be a multiple of three!"); - - return Vec(torch::tensor(values, default_tensor_options()).reshape({-1, 3})); -} -} // namespace neml2 diff --git a/src/neml2/tensors/user_tensors/FillR2.cxx b/src/neml2/tensors/user_tensors/FillR2.cxx index 759b630112..23029b9453 100644 --- a/src/neml2/tensors/user_tensors/FillR2.cxx +++ b/src/neml2/tensors/user_tensors/FillR2.cxx @@ -32,7 +32,16 @@ OptionSet FillR2::expected_options() { OptionSet options = UserTensor::expected_options(); + options.doc() = "Construct a R2 with a vector of Scalars. The vector length must be 1, 3, 6, or " + "9. When vector length is 1, the Scalar value is used to fill the diagonals; " + "when vector length is 3, the Scalar values are used to fill the respective " + "diagonal entries; when vector length is 6, the Scalar values are used to fill " + "the tensor following the Voigt notation; when vector length is 9, the Scalar " + "values are used to fill the tensor in the row-major fashion."; + options.set>>("values"); + options.set("values").doc() = "Scalars used to fill the R2"; + return options; } diff --git a/src/neml2/tensors/user_tensors/FillRot.cxx b/src/neml2/tensors/user_tensors/FillRot.cxx index e7fa49e788..6f355c4881 100644 --- a/src/neml2/tensors/user_tensors/FillRot.cxx +++ b/src/neml2/tensors/user_tensors/FillRot.cxx @@ -32,8 +32,14 @@ OptionSet FillRot::expected_options() { OptionSet options = UserTensor::expected_options(); + options.doc() = "Construct a Rot from a vector of Scalars."; + options.set>>("values"); + options.set("values").doc() = "Scalars used to fill the Rot"; + options.set("method") = "modified"; + options.set("method").doc() = "Fill method, options are 'modified' and 'standard'."; + return options; } diff --git a/src/neml2/tensors/user_tensors/FillSR2.cxx b/src/neml2/tensors/user_tensors/FillSR2.cxx index 7ac486c8aa..b6140b0693 100644 --- a/src/neml2/tensors/user_tensors/FillSR2.cxx +++ b/src/neml2/tensors/user_tensors/FillSR2.cxx @@ -32,7 +32,16 @@ OptionSet FillSR2::expected_options() { OptionSet options = UserTensor::expected_options(); + options.doc() = "Construct a R2 with a vector of Scalars. The vector length must be 1, 3, or 6. " + "See the full documentation on neml2::SR2 on the dispatch of fill method. When " + "vector length is 1, the Scalar value is used to fill the diagonals; when vector " + "length is 3, the Scalar values are used to fill the respective diagonal " + "entries; when vector length is 6, the Scalar values are used to fill the tensor " + "following the Voigt notation."; + options.set>>("values"); + options.set("values").doc() = "Scalars used to fill the R2"; + return options; } diff --git a/src/neml2/tensors/user_tensors/FillWR2.cxx b/src/neml2/tensors/user_tensors/FillWR2.cxx index 83676b5b64..e11042c2f1 100644 --- a/src/neml2/tensors/user_tensors/FillWR2.cxx +++ b/src/neml2/tensors/user_tensors/FillWR2.cxx @@ -32,7 +32,11 @@ OptionSet FillWR2::expected_options() { OptionSet options = UserTensor::expected_options(); + options.doc() = "Construct a Rot from a vector of Scalars."; + options.set>>("values"); + options.set("values").doc() = "Scalars used to fill the WR2"; + return options; } diff --git a/src/neml2/tensors/user_tensors/FullBatchTensor.cxx b/src/neml2/tensors/user_tensors/FullBatchTensor.cxx index 158319b0ff..bccfe65d51 100644 --- a/src/neml2/tensors/user_tensors/FullBatchTensor.cxx +++ b/src/neml2/tensors/user_tensors/FullBatchTensor.cxx @@ -32,9 +32,18 @@ OptionSet FullBatchTensor::expected_options() { OptionSet options = UserTensor::expected_options(); + options.doc() = + "Construct a full BatchTensor with given batch and base shapes filled with a given value."; + options.set("batch_shape") = {}; + options.set("batch_shape").doc() = "Batch shape"; + options.set("base_shape") = {}; + options.set("base_shape").doc() = "Base shape"; + options.set("value"); + options.set("value").doc() = "Value used to fill the tensor"; + return options; } diff --git a/src/neml2/tensors/user_tensors/FullFixedDimTensor.cxx b/src/neml2/tensors/user_tensors/FullFixedDimTensor.cxx index 6beddea823..79943cd403 100644 --- a/src/neml2/tensors/user_tensors/FullFixedDimTensor.cxx +++ b/src/neml2/tensors/user_tensors/FullFixedDimTensor.cxx @@ -33,9 +33,20 @@ template OptionSet FullFixedDimTensor::expected_options() { + // This is the only way of getting tensor type in a static method like this... + // Trim 6 chars to remove 'neml2::' + auto tensor_type = utils::demangle(typeid(T).name()).substr(7); + OptionSet options = UserTensor::expected_options(); + options.doc() = + "Construct a full " + tensor_type + " with given batch shape filled with a given value."; + options.set("batch_shape") = {}; + options.set("batch_shape").doc() = "Batch shape"; + options.set("value"); + options.set("value").doc() = "Value used to fill the tensor"; + return options; } diff --git a/src/neml2/tensors/user_tensors/IdentityBatchTensor.cxx b/src/neml2/tensors/user_tensors/IdentityBatchTensor.cxx index 0834ed03b9..ec83bee80f 100644 --- a/src/neml2/tensors/user_tensors/IdentityBatchTensor.cxx +++ b/src/neml2/tensors/user_tensors/IdentityBatchTensor.cxx @@ -32,8 +32,15 @@ OptionSet IdentityBatchTensor::expected_options() { OptionSet options = UserTensor::expected_options(); + options.doc() = "Construct an identity BatchTensor with given batch shape."; + options.set("batch_shape") = {}; + options.set("batch_shape").doc() = "Batch shape"; + options.set("n"); + options.set("n").doc() = + "Diagonal size of the identity tensor, i.e., base shape of the identity tensor will be (n,n)"; + return options; } diff --git a/src/neml2/tensors/user_tensors/LinspaceBatchTensor.cxx b/src/neml2/tensors/user_tensors/LinspaceBatchTensor.cxx index 88380ae8b6..8814c6ac40 100644 --- a/src/neml2/tensors/user_tensors/LinspaceBatchTensor.cxx +++ b/src/neml2/tensors/user_tensors/LinspaceBatchTensor.cxx @@ -33,12 +33,28 @@ OptionSet LinspaceBatchTensor::expected_options() { OptionSet options = UserTensor::expected_options(); + options.doc() = "Construct a BatchTensor linearly spaced on the batch dimensions. See " + "neml2::BatchTensorBase::linspace for a detailed explanation."; + options.set>("start"); + options.set("start").doc() = "The starting tensor"; + options.set>("end"); + options.set("end").doc() = "The ending tensor"; + options.set("nstep"); + options.set("nstep").doc() = "The number of steps with even spacing along the new dimension"; + options.set("dim") = 0; + options.set("dim").doc() = "Where to insert the new dimension"; + options.set("batch_dim") = -1; + options.set("batch_dim").doc() = "Batch dimension of the output"; + options.set("batch_expand") = TorchShape(); + options.set("batch_expand").doc() = "After construction, perform an additional batch expanding " + "operation into the given batch shape."; + return options; } diff --git a/src/neml2/tensors/user_tensors/LinspaceFixedDimTensor.cxx b/src/neml2/tensors/user_tensors/LinspaceFixedDimTensor.cxx index 705e742b27..e81aba5081 100644 --- a/src/neml2/tensors/user_tensors/LinspaceFixedDimTensor.cxx +++ b/src/neml2/tensors/user_tensors/LinspaceFixedDimTensor.cxx @@ -33,12 +33,30 @@ template OptionSet LinspaceFixedDimTensor::expected_options() { + // This is the only way of getting tensor type in a static method like this... + // Trim 6 chars to remove 'neml2::' + auto tensor_type = utils::demangle(typeid(T).name()).substr(7); + OptionSet options = UserTensor::expected_options(); + options.doc() = "Construct a " + tensor_type + + " linearly spaced on the batch dimensions. See neml2::BatchTensorBase::linspace " + "for a detailed explanation."; + options.set>("start"); + options.set("start").doc() = "The starting tensor"; + options.set>("end"); + options.set("end").doc() = "The ending tensor"; + options.set("nstep"); + options.set("nstep").doc() = "The number of steps with even spacing along the new dimension"; + options.set("dim") = 0; + options.set("dim").doc() = "Where to insert the new dimension"; + options.set("batch_dim") = -1; + options.set("batch_dim").doc() = "Batch dimension of the output"; + return options; } diff --git a/src/neml2/tensors/user_tensors/LogspaceBatchTensor.cxx b/src/neml2/tensors/user_tensors/LogspaceBatchTensor.cxx index aac596842e..b79b654728 100644 --- a/src/neml2/tensors/user_tensors/LogspaceBatchTensor.cxx +++ b/src/neml2/tensors/user_tensors/LogspaceBatchTensor.cxx @@ -33,12 +33,27 @@ OptionSet LogspaceBatchTensor::expected_options() { OptionSet options = UserTensor::expected_options(); + options.doc() = "Construct a BatchTensor with exponents linearly spaced on the batch dimensions. " + "See neml2::BatchTensorBase::logspace for a detailed explanation."; + options.set>("start"); + options.set("start").doc() = "The starting tensor"; + options.set>("end"); + options.set("end").doc() = "The ending tensor"; + options.set("nstep"); + options.set("nstep").doc() = "The number of steps with even spacing along the new dimension"; + options.set("dim") = 0; - options.set("base") = 10; + options.set("dim").doc() = "Where to insert the new dimension"; + options.set("batch_dim") = -1; + options.set("batch_dim").doc() = "Batch dimension of the output"; + + options.set("base") = 10; + options.set("base").doc() = "Exponent base"; + return options; } diff --git a/src/neml2/tensors/user_tensors/LogspaceFixedDimTensor.cxx b/src/neml2/tensors/user_tensors/LogspaceFixedDimTensor.cxx index 0a6c939606..fecec9edc4 100644 --- a/src/neml2/tensors/user_tensors/LogspaceFixedDimTensor.cxx +++ b/src/neml2/tensors/user_tensors/LogspaceFixedDimTensor.cxx @@ -33,13 +33,33 @@ template OptionSet LogspaceFixedDimTensor::expected_options() { + // This is the only way of getting tensor type in a static method like this... + // Trim 6 chars to remove 'neml2::' + auto tensor_type = utils::demangle(typeid(T).name()).substr(7); + OptionSet options = UserTensor::expected_options(); + options.doc() = "Construct a " + tensor_type + + " with exponents linearly spaced on the batch dimensions. See " + "neml2::BatchTensorBase::logspace for a detailed explanation."; + options.set>("start"); + options.set("start").doc() = "The starting tensor"; + options.set>("end"); + options.set("end").doc() = "The ending tensor"; + options.set("nstep"); + options.set("nstep").doc() = "The number of steps with even spacing along the new dimension"; + options.set("dim") = 0; + options.set("dim").doc() = "Where to insert the new dimension"; + options.set("batch_dim") = -1; + options.set("batch_dim").doc() = "Batch dimension of the output"; + options.set("base") = 10; + options.set("base").doc() = "Exponent base"; + return options; } diff --git a/src/neml2/tensors/user_tensors/OnesBatchTensor.cxx b/src/neml2/tensors/user_tensors/OnesBatchTensor.cxx index 465dc16ad4..e4c27071fa 100644 --- a/src/neml2/tensors/user_tensors/OnesBatchTensor.cxx +++ b/src/neml2/tensors/user_tensors/OnesBatchTensor.cxx @@ -32,8 +32,14 @@ OptionSet OnesBatchTensor::expected_options() { OptionSet options = UserTensor::expected_options(); + options.doc() = "Construct a BatchTensor with batch and base shapes filled with ones."; + options.set("batch_shape") = {}; + options.set("batch_shape").doc() = "Batch shape"; + options.set("base_shape") = {}; + options.set("base_shape").doc() = "Base shape"; + return options; } diff --git a/src/neml2/tensors/user_tensors/OnesFixedDimTensor.cxx b/src/neml2/tensors/user_tensors/OnesFixedDimTensor.cxx index 7803256300..1da5e22df4 100644 --- a/src/neml2/tensors/user_tensors/OnesFixedDimTensor.cxx +++ b/src/neml2/tensors/user_tensors/OnesFixedDimTensor.cxx @@ -33,8 +33,16 @@ template OptionSet OnesFixedDimTensor::expected_options() { + // This is the only way of getting tensor type in a static method like this... + // Trim 6 chars to remove 'neml2::' + auto tensor_type = utils::demangle(typeid(T).name()).substr(7); + OptionSet options = UserTensor::expected_options(); + options.doc() = "Construct a " + tensor_type + " with given batch shape filled with ones."; + options.set("batch_shape") = {}; + options.set("batch_shape").doc() = "Batch shape"; + return options; } diff --git a/src/neml2/tensors/user_tensors/UserBatchTensor.cxx b/src/neml2/tensors/user_tensors/UserBatchTensor.cxx index d91cfa961f..e602d379ab 100644 --- a/src/neml2/tensors/user_tensors/UserBatchTensor.cxx +++ b/src/neml2/tensors/user_tensors/UserBatchTensor.cxx @@ -32,9 +32,18 @@ OptionSet UserBatchTensor::expected_options() { OptionSet options = UserTensor::expected_options(); + options.doc() = "Construct a BatchTensor from a vector of values. The vector will be reshaped " + "according to the specified batch and base shapes."; + options.set>("values"); + options.set("values").doc() = "Values in this (flattened) tensor"; + options.set("batch_shape") = {}; + options.set("batch_shape").doc() = "Batch shape"; + options.set("base_shape") = {}; + options.set("base_shape").doc() = "Base shape"; + return options; } diff --git a/src/neml2/tensors/user_tensors/UserFixedDimTensor.cxx b/src/neml2/tensors/user_tensors/UserFixedDimTensor.cxx index 2fe3374e17..543dca7a29 100644 --- a/src/neml2/tensors/user_tensors/UserFixedDimTensor.cxx +++ b/src/neml2/tensors/user_tensors/UserFixedDimTensor.cxx @@ -33,9 +33,21 @@ template OptionSet UserFixedDimTensor::expected_options() { + // This is the only way of getting tensor type in a static method like this... + // Trim 6 chars to remove 'neml2::' + auto tensor_type = utils::demangle(typeid(T).name()).substr(7); + OptionSet options = UserTensor::expected_options(); + options.doc() = + "Construct a " + tensor_type + + " from a vector values. The vector will be reshaped according to the specified batch shape."; + options.set>("values"); + options.set("values").doc() = "Values in this (flattened) tensor"; + options.set("batch_shape") = {}; + options.set("batch_shape").doc() = "Batch shape"; + return options; } diff --git a/src/neml2/tensors/user_tensors/ZerosBatchTensor.cxx b/src/neml2/tensors/user_tensors/ZerosBatchTensor.cxx index 64acf93ea6..bb2adc66f8 100644 --- a/src/neml2/tensors/user_tensors/ZerosBatchTensor.cxx +++ b/src/neml2/tensors/user_tensors/ZerosBatchTensor.cxx @@ -32,8 +32,14 @@ OptionSet ZerosBatchTensor::expected_options() { OptionSet options = UserTensor::expected_options(); + options.doc() = "Construct a BatchTensor with batch and base shapes filled with zeros."; + options.set("batch_shape") = {}; + options.set("batch_shape").doc() = "Batch shape"; + options.set("base_shape") = {}; + options.set("base_shape").doc() = "Base shape"; + return options; } diff --git a/src/neml2/tensors/user_tensors/ZerosFixedDimTensor.cxx b/src/neml2/tensors/user_tensors/ZerosFixedDimTensor.cxx index f24e533180..690588d967 100644 --- a/src/neml2/tensors/user_tensors/ZerosFixedDimTensor.cxx +++ b/src/neml2/tensors/user_tensors/ZerosFixedDimTensor.cxx @@ -33,8 +33,16 @@ template OptionSet ZerosFixedDimTensor::expected_options() { + // This is the only way of getting tensor type in a static method like this... + // Trim 6 chars to remove 'neml2::' + auto tensor_type = utils::demangle(typeid(T).name()).substr(7); + OptionSet options = UserTensor::expected_options(); + options.doc() = "Construct a " + tensor_type + " with given batch shape filled with zeros."; + options.set("batch_shape") = {}; + options.set("batch_shape").doc() = "Batch shape"; + return options; } diff --git a/tests/unit/crystallography/test_CrystalGeometry.i b/tests/unit/crystallography/test_CrystalGeometry.i index cfd9527d78..b9bb005ab4 100644 --- a/tests/unit/crystallography/test_CrystalGeometry.i +++ b/tests/unit/crystallography/test_CrystalGeometry.i @@ -1,10 +1,10 @@ [Data] [scgeom] type = CrystalGeometry - crystal_class = "class_432" - lattice_vectors = "lvecs" - slip_directions = "sdirs" - slip_planes = "splanes" + crystal_class = 'class_432' + lattice_vectors = 'lvecs' + slip_directions = 'sdirs' + slip_planes = 'splanes' [] [] @@ -18,15 +18,14 @@ values = '1 1 1' [] [lvecs] - type = Fill3DVec - values = ' - 1.2 0.0 0.0 - 0.0 1.2 0.0 - 0.0 0.0 1.2 - ' + type = Vec + batch_shape = '(3)' + values = "1.2 0.0 0.0 + 0.0 1.2 0.0 + 0.0 0.0 1.2" [] [class_432] type = SymmetryFromOrbifold - orbifold = "432" + orbifold = 432 [] -[] \ No newline at end of file +[] diff --git a/tests/unit/tensors/user_tensors/test_Fill3DVec.cxx b/tests/unit/tensors/user_tensors/test_Fill3DVec.cxx deleted file mode 100644 index b8e1581b09..0000000000 --- a/tests/unit/tensors/user_tensors/test_Fill3DVec.cxx +++ /dev/null @@ -1,47 +0,0 @@ -// Copyright 2023, UChicago Argonne, LLC -// All Rights Reserved -// Software Name: NEML2 -- the New Engineering material Model Library, version 2 -// By: Argonne National Laboratory -// OPEN SOURCE LICENSE (MIT) -// -// Permission is hereby granted, free of charge, to any person obtaining a copy -// of this software and associated documentation files (the "Software"), to deal -// in the Software without restriction, including without limitation the rights -// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -// copies of the Software, and to permit persons to whom the Software is -// furnished to do so, subject to the following conditions: -// -// The above copyright notice and this permission notice shall be included in -// all copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -// THE SOFTWARE. - -#include - -#include "utils.h" -#include "neml2/tensors/user_tensors/Fill3DVec.h" - -using namespace neml2; - -TEST_CASE("Fill3DVec", "[tensors/user_tensors]") -{ - load_model("unit/tensors/user_tensors/test_Fill3DVec.i"); - - const auto valid_1 = Factory::get_object_ptr("Tensors", "v1"); - const auto correct_1 = Vec::fill(1.0, 2.0, 3.0); - REQUIRE(torch::allclose(*valid_1, correct_1)); - - const auto valid_4 = Factory::get_object_ptr("Tensors", "v4"); - const auto correct_4 = - Vec(torch::tensor({{1.0, 2.0, 3.0}, {4.0, 5.0, 6.0}, {7.0, 8.0, 9.0}, {10.0, 11.0, 12.0}}, - default_tensor_options())); - REQUIRE(torch::allclose(*valid_4, correct_4)); - - REQUIRE_THROWS(Factory::get_object_ptr("Tensors", "invalid")); -} diff --git a/tests/unit/tensors/user_tensors/test_Fill3DVec.i b/tests/unit/tensors/user_tensors/test_Fill3DVec.i deleted file mode 100644 index 0ec988fb19..0000000000 --- a/tests/unit/tensors/user_tensors/test_Fill3DVec.i +++ /dev/null @@ -1,14 +0,0 @@ -[Tensors] - [v1] - type = Fill3DVec - values = '1 2 3' - [] - [v4] - type = Fill3DVec - values = '1 2 3 4 5 6 7 8 9 10 11 12' - [] - [invalid] - type = Fill3DVec - values = '1 2 3 4 5 6 7 8 9 10 11' - [] -[] From 04aa2c9b697f543fee59101d13ab76adbaf3ec8b Mon Sep 17 00:00:00 2001 From: Gary Hu Date: Wed, 12 Jun 2024 21:05:30 -0500 Subject: [PATCH 47/55] Documented all models in the framework (excluding physics modules) --- include/neml2/models/Interpolation.h | 14 ++++++++++++++ scripts/syntax_to_md.py | 2 +- src/neml2/models/ArrheniusParameter.cxx | 11 ++++++----- .../models/BackwardEulerTimeIntegration.cxx | 13 +++++++++++++ src/neml2/models/ComposedModel.cxx | 16 ++++++++++++++++ src/neml2/models/CopyVariable.cxx | 6 ++++++ src/neml2/models/ForceRate.cxx | 8 ++++++++ src/neml2/models/ForwardEulerTimeIntegration.cxx | 10 ++++++++++ src/neml2/models/ImplicitUpdate.cxx | 8 ++++++++ src/neml2/models/LinearInterpolation.cxx | 1 + src/neml2/models/RotationMatrix.cxx | 7 +++++++ src/neml2/models/SR2Invariant.cxx | 8 ++++++++ src/neml2/models/StateRate.cxx | 8 ++++++++ src/neml2/models/SumModel.cxx | 14 ++++++++++++++ .../WR2ExplicitExponentialTimeIntegration.cxx | 8 ++++++++ .../WR2ImplicitExponentialTimeIntegration.cxx | 10 ++++++++++ 16 files changed, 138 insertions(+), 6 deletions(-) diff --git a/include/neml2/models/Interpolation.h b/include/neml2/models/Interpolation.h index a30204efd9..5133ed43fb 100644 --- a/include/neml2/models/Interpolation.h +++ b/include/neml2/models/Interpolation.h @@ -67,10 +67,24 @@ template OptionSet Interpolation::expected_options() { + // This is the only way of getting tensor type in a static method like this... + // Trim 6 chars to remove 'neml2::' + auto tensor_type = utils::demangle(typeid(T).name()).substr(7); + OptionSet options = NonlinearParameter::expected_options(); + options.doc() = "Interpolate a " + tensor_type + + " as a function of the given argument. See neml2::Interpolation for rules on " + "shapes of the interpolant and the argument."; + options.set("argument"); + options.set("argument").doc() = "Argument used to query the interpolant"; + options.set>("abscissa"); + options.set("abscissa").doc() = "Scalar defining the abscissa values of the interpolant"; + options.set>("ordinate"); + options.set("ordinate").doc() = tensor_type + " defining the ordinate values of the interpolant"; + return options; } diff --git a/scripts/syntax_to_md.py b/scripts/syntax_to_md.py index 59443f66c4..47a10aab91 100755 --- a/scripts/syntax_to_md.py +++ b/scripts/syntax_to_md.py @@ -84,7 +84,7 @@ def get_sections(syntax): "### {} {{#{}}}\n\n".format(input_type, input_type.lower()) ) if params["doc"]: - stream.write("_{}_\n".format(params["doc"])) + stream.write("{}\n".format(params["doc"])) for param_name, info in params.items(): if param_name == "section": continue diff --git a/src/neml2/models/ArrheniusParameter.cxx b/src/neml2/models/ArrheniusParameter.cxx index 0c16fba3d1..3a6af7aed5 100644 --- a/src/neml2/models/ArrheniusParameter.cxx +++ b/src/neml2/models/ArrheniusParameter.cxx @@ -33,20 +33,21 @@ ArrheniusParameter::expected_options() { OptionSet options = NonlinearParameter::expected_options(); options.doc() = "Define the nonlinear parameter as a function of temperature according to the " - "Arrhenius law. The nonlinear parameter is therefore parametrized by the " - "reference value and the activation energy."; + "Arrhenius law \\f$ p = p_0 \\exp \\left( -\\frac{Q}{RT} \\right) \\f$, where " + "\\f$ p_0 \\f$ is the reference value, \\f$ Q \\f$ is the activation energy, " + "\\f$ R \\f$ is the ideal gas constant, and \\f$ T \\f$ is the temperature."; options.set>("reference_value"); - options.set("reference_value").doc() = "Reference value of the parameter"; + options.set("reference_value").doc() = "Reference value"; options.set>("activation_energy"); - options.set("activation_energy").doc() = "Activation energy in the Arrhenius law"; + options.set("activation_energy").doc() = "Activation energy"; options.set("ideal_gas_constant"); options.set("ideal_gas_constant").doc() = "The ideal gas constant"; options.set("temperature") = VariableName("forces", "T"); - options.set("temperature").doc() = "Variable name for the temperature"; + options.set("temperature").doc() = "Temperature"; return options; } diff --git a/src/neml2/models/BackwardEulerTimeIntegration.cxx b/src/neml2/models/BackwardEulerTimeIntegration.cxx index a34803b0ec..cd4521bc09 100644 --- a/src/neml2/models/BackwardEulerTimeIntegration.cxx +++ b/src/neml2/models/BackwardEulerTimeIntegration.cxx @@ -35,10 +35,23 @@ OptionSet BackwardEulerTimeIntegration::expected_options() { OptionSet options = Model::expected_options(); + options.doc() = + "Define the backward Euler time integration residual \\f$ r = s - s_n - (t - t_n) \\dot{s} " + "\\f$, where \\f$s\\f$ is the variable being integrated, \\f$\\dot{s}\\f$ is the variable " + "rate, and \\f$t\\f$ is time. Subscripts \\f$n\\f$ denote quantities from the previous time " + "step."; + NonlinearSystem::enable_automatic_scaling(options); + options.set("variable"); + options.set("variable").doc() = "Variable being integrated"; + options.set("variable_rate"); + options.set("variable_rate").doc() = "Variable rate"; + options.set("time") = VariableName("t"); + options.set("time").doc() = "Time"; + return options; } diff --git a/src/neml2/models/ComposedModel.cxx b/src/neml2/models/ComposedModel.cxx index e4564c1a71..b73f3e4bf4 100644 --- a/src/neml2/models/ComposedModel.cxx +++ b/src/neml2/models/ComposedModel.cxx @@ -32,10 +32,26 @@ OptionSet ComposedModel::expected_options() { OptionSet options = Model::expected_options(); + options.doc() = + "Compose multiple models together to form a single model. The composed model can then be " + "treated as a new model and composed with others. The [system documentation](@ref " + "model-composition) provides in-depth explanation on how the models are composed together."; + NonlinearSystem::enable_automatic_scaling(options); + options.set>("models"); + options.set("models").doc() = "Models being composed together"; + options.set>("additional_outputs"); + options.set("additional_outputs").doc() = + "Extra output variables to be extracted from the composed model in addition to the ones " + "identified through dependency resolution."; + options.set>("priority"); + options.set("priority").doc() = + "Priorities of models in decreasing order. A model with higher priority will be evaluated " + "first. This is useful for breaking cyclic dependency."; + return options; } diff --git a/src/neml2/models/CopyVariable.cxx b/src/neml2/models/CopyVariable.cxx index 45579a2dba..d2d9a91ccc 100644 --- a/src/neml2/models/CopyVariable.cxx +++ b/src/neml2/models/CopyVariable.cxx @@ -34,8 +34,14 @@ OptionSet CopyVariable::expected_options() { OptionSet options = Model::expected_options(); + options.doc() = "Copy the value from one variable to another."; + options.set("from"); + options.set("from").doc() = "Variable to copy value from"; + options.set("to"); + options.set("to").doc() = "Variable to copy value to"; + return options; } diff --git a/src/neml2/models/ForceRate.cxx b/src/neml2/models/ForceRate.cxx index 5af7a64fe1..75f6ce2cfc 100644 --- a/src/neml2/models/ForceRate.cxx +++ b/src/neml2/models/ForceRate.cxx @@ -35,8 +35,16 @@ OptionSet ForceRate::expected_options() { OptionSet options = Model::expected_options(); + options.doc() = "Calculate the first order discrete time derivative of a force variable as \\f$ " + "\\dot{f} = \\frac{f-f_n}{t-t_n} \\f$, where \\f$ f \\f$ is the force variable, " + "and \\f$ t \\f$ is time."; + options.set("force"); + options.set("force").doc() = "The force variable to take time derivative with"; + options.set("time") = {"t"}; + options.set("time").doc() = "Time"; + return options; } diff --git a/src/neml2/models/ForwardEulerTimeIntegration.cxx b/src/neml2/models/ForwardEulerTimeIntegration.cxx index 60f5db73f5..d40fe4cabd 100644 --- a/src/neml2/models/ForwardEulerTimeIntegration.cxx +++ b/src/neml2/models/ForwardEulerTimeIntegration.cxx @@ -35,8 +35,18 @@ OptionSet ForwardEulerTimeIntegration::expected_options() { OptionSet options = Model::expected_options(); + options.doc() = + "Perform forward Euler time integration defined as \\f$ s = s_n + (t - t_n) \\dot{s} " + "\\f$, where \\f$s\\f$ is the variable being integrated, \\f$\\dot{s}\\f$ is the variable " + "rate, and \\f$t\\f$ is time. Subscripts \\f$n\\f$ denote quantities from the previous time " + "step."; + options.set("variable"); + options.set("variable").doc() = "Variable being integrated"; + options.set("time") = VariableName("t"); + options.set("time").doc() = "Time"; + return options; } diff --git a/src/neml2/models/ImplicitUpdate.cxx b/src/neml2/models/ImplicitUpdate.cxx index 3d2414bdaf..7f6f382e77 100644 --- a/src/neml2/models/ImplicitUpdate.cxx +++ b/src/neml2/models/ImplicitUpdate.cxx @@ -33,8 +33,16 @@ OptionSet ImplicitUpdate::expected_options() { OptionSet options = Model::expected_options(); + options.doc() = + "Update an implicit model by solving the underlying implicit system of equations."; + options.set("implicit_model"); + options.set("implicit_model").doc() = + "The implicit model defining the implicit system of equations to be solved"; + options.set("solver"); + options.set("solver").doc() = "Solver used to solve the implicit system"; + return options; } diff --git a/src/neml2/models/LinearInterpolation.cxx b/src/neml2/models/LinearInterpolation.cxx index f77827cda6..dfaec5b879 100644 --- a/src/neml2/models/LinearInterpolation.cxx +++ b/src/neml2/models/LinearInterpolation.cxx @@ -37,6 +37,7 @@ OptionSet LinearInterpolation::expected_options() { OptionSet options = Interpolation::expected_options(); + options.doc() += " This object performs a _linear interpolation_."; return options; } diff --git a/src/neml2/models/RotationMatrix.cxx b/src/neml2/models/RotationMatrix.cxx index 46ed019a9f..c54bc9ab13 100644 --- a/src/neml2/models/RotationMatrix.cxx +++ b/src/neml2/models/RotationMatrix.cxx @@ -32,8 +32,15 @@ OptionSet RotationMatrix::expected_options() { OptionSet options = Model::expected_options(); + options.doc() = + "Convert a Rot (rotation represented in Rodrigues format) to R2 (a full rotation matrix)."; + options.set("from"); + options.set("from").doc() = "Rot to convert"; + options.set("to"); + options.set("to").doc() = "R2 to store the resulting rotation matrix"; + return options; } diff --git a/src/neml2/models/SR2Invariant.cxx b/src/neml2/models/SR2Invariant.cxx index 38cf72cbb5..82a2e8df01 100644 --- a/src/neml2/models/SR2Invariant.cxx +++ b/src/neml2/models/SR2Invariant.cxx @@ -33,9 +33,17 @@ OptionSet SR2Invariant::expected_options() { OptionSet options = Model::expected_options(); + options.doc() = "Calculate the invariant of a symmetric second order tensor (of type SR2)."; + options.set("tensor"); + options.set("tensor").doc() = "SR2 which is used to calculate the invariant of"; + options.set("invariant"); + options.set("invariant").doc() = "Invariant"; + options.set("invariant_type"); + options.set("invariant_type").doc() = "Type of invariant. Options are I1, I2, and VONMISES."; + return options; } diff --git a/src/neml2/models/StateRate.cxx b/src/neml2/models/StateRate.cxx index 7efdb018dd..5e92cc7162 100644 --- a/src/neml2/models/StateRate.cxx +++ b/src/neml2/models/StateRate.cxx @@ -35,8 +35,16 @@ OptionSet StateRate::expected_options() { OptionSet options = Model::expected_options(); + options.doc() = "Calculate the first order discrete time derivative of a state variable as \\f$ " + "\\dot{s} = \\frac{s-s_n}{t-t_n} \\f$, where \\f$ s \\f$ is the state variable, " + "and \\f$ t \\f$ is time."; + options.set("state"); + options.set("state").doc() = "The state variable to take time derivative with"; + options.set("time") = VariableName("t"); + options.set("time").doc() = "Time"; + return options; } diff --git a/src/neml2/models/SumModel.cxx b/src/neml2/models/SumModel.cxx index 7a69b9dbae..a039f6c3d3 100644 --- a/src/neml2/models/SumModel.cxx +++ b/src/neml2/models/SumModel.cxx @@ -34,10 +34,24 @@ template OptionSet SumModel::expected_options() { + // This is the only way of getting tensor type in a static method like this... + // Trim 6 chars to remove 'neml2::' + auto tensor_type = utils::demangle(typeid(T).name()).substr(7); + OptionSet options = Model::expected_options(); + options.doc() = "Calculate linear combination of multiple " + tensor_type + + " tensors as \\f$ u = c_i v_i \\f$ (Einstein summation assumed), where \\f$ c_i " + "\\f$ are the coefficients, and \\f$ v_i \\f$ are the variables to be summed."; + options.set>("from_var"); + options.set("from_var").doc() = tensor_type + " tensors to be summed"; + options.set("to_var"); + options.set("to_var").doc() = "The sum"; + options.set>>("coefficients") = {}; + options.set("coefficients").doc() = "Weights associated with each variable"; + return options; } diff --git a/src/neml2/models/WR2ExplicitExponentialTimeIntegration.cxx b/src/neml2/models/WR2ExplicitExponentialTimeIntegration.cxx index be456d1909..90697a7e92 100644 --- a/src/neml2/models/WR2ExplicitExponentialTimeIntegration.cxx +++ b/src/neml2/models/WR2ExplicitExponentialTimeIntegration.cxx @@ -33,8 +33,16 @@ OptionSet WR2ExplicitExponentialTimeIntegration::expected_options() { OptionSet options = Model::expected_options(); + options.doc() = "Perform explicit discrete exponential time integration of a rotation. The " + "update can be written as \\f$ s = \\exp\\left[ (t-t_n)\\dot{s}\\right] \\circ " + "s_n \\f$, where \\f$ \\circ \\f$ denotes the rotation operator."; + options.set("variable"); + options.set("variable").doc() = "Variable being integrated"; + options.set("time") = VariableName("t"); + options.set("time").doc() = "Time"; + return options; } diff --git a/src/neml2/models/WR2ImplicitExponentialTimeIntegration.cxx b/src/neml2/models/WR2ImplicitExponentialTimeIntegration.cxx index d693b04104..7d3cb811fd 100644 --- a/src/neml2/models/WR2ImplicitExponentialTimeIntegration.cxx +++ b/src/neml2/models/WR2ImplicitExponentialTimeIntegration.cxx @@ -37,9 +37,19 @@ OptionSet WR2ImplicitExponentialTimeIntegration::expected_options() { OptionSet options = Model::expected_options(); + options.doc() = "Define the implicit discrete exponential time integration residual of a " + "rotation variable. The residual can be written as \\f$ r = s - \\exp\\left[ " + "(t-t_n)\\dot{s}\\right] \\circ s_n \\f$, where \\f$ \\circ \\f$ denotes the " + "rotation operator."; + NonlinearSystem::enable_automatic_scaling(options); + options.set("variable"); + options.set("variable").doc() = "Variable being integrated"; + options.set("time") = VariableName("t"); + options.set("time").doc() = "Time"; + return options; } From 7b96df7e0e18b57c00f40dd2f2067ca66eae8019 Mon Sep 17 00:00:00 2001 From: Gary Hu Date: Thu, 13 Jun 2024 13:54:25 -0500 Subject: [PATCH 48/55] Documented all solid mechanics models (excluding crystal plasticity) and solvers --- doc/content/dev.md | 2 +- doc/content/system/model.md | 2 +- .../ChabochePlasticHardening.h | 4 +-- .../solid_mechanics/KinematicHardening.h | 2 +- .../neml2/models/solid_mechanics/OverStress.h | 2 +- .../AssociativeIsotropicPlasticHardening.cxx | 11 +++++++ .../AssociativeKinematicPlasticHardening.cxx | 12 ++++++++ .../AssociativePlasticFlow.cxx | 11 +++++++ .../ChabochePlasticHardening.cxx | 22 ++++++++++++++ .../models/solid_mechanics/Eigenstrain.cxx | 3 ++ .../models/solid_mechanics/ElasticStrain.cxx | 12 ++++++++ .../models/solid_mechanics/Elasticity.cxx | 14 +++++++++ src/neml2/models/solid_mechanics/FlowRule.cxx | 5 ++++ .../solid_mechanics/GTNYieldFunction.cxx | 29 +++++++++++++++++++ .../solid_mechanics/GursonCavitation.cxx | 9 ++++++ .../solid_mechanics/IsotropicHardening.cxx | 6 ++++ .../solid_mechanics/IsotropicMandelStress.cxx | 3 ++ .../solid_mechanics/KinematicHardening.cxx | 6 ++++ .../LinearIsotropicElasticity.cxx | 10 +++++++ .../LinearIsotropicHardening.cxx | 5 ++++ .../LinearKinematicHardening.cxx | 5 ++++ .../models/solid_mechanics/MandelStress.cxx | 6 ++++ .../models/solid_mechanics/Normality.cxx | 11 +++++++ .../OlevskySinteringStress.cxx | 14 +++++++++ .../models/solid_mechanics/OverStress.cxx | 10 +++++++ .../PerzynaPlasticFlowRate.cxx | 10 +++++++ .../solid_mechanics/PlasticFlowRate.cxx | 5 ++++ .../RateIndependentPlasticFlowConstraint.cxx | 12 ++++++++ .../solid_mechanics/ThermalEigenstrain.cxx | 12 ++++++++ .../models/solid_mechanics/TotalStrain.cxx | 10 +++++++ .../VoceIsotropicHardening.cxx | 8 +++++ .../models/solid_mechanics/YieldFunction.cxx | 13 +++++++++ src/neml2/solvers/Newton.cxx | 2 ++ src/neml2/solvers/NewtonWithLineSearch.cxx | 12 ++++++++ src/neml2/solvers/NewtonWithTrustRegion.cxx | 29 +++++++++++++++++++ src/neml2/solvers/NonlinearSolver.cxx | 8 +++++ src/neml2/solvers/NonlinearSystem.cxx | 13 +++++++++ src/neml2/solvers/Solver.cxx | 3 ++ 38 files changed, 347 insertions(+), 6 deletions(-) diff --git a/doc/content/dev.md b/doc/content/dev.md index 4171b34b26..ac900842f8 100644 --- a/doc/content/dev.md +++ b/doc/content/dev.md @@ -9,7 +9,7 @@ The following tutorials serve as a developer-facing step-by-step guide for creat k &= H \bar{\varepsilon}^p, \f} where \f$\bar{\varepsilon}^p\f$ is the equivalent plastic strain and \f$k\f$ is the isotropic hardening. The input variable for this model is -\f$\mathbf{\varepsilon}\f$, the output variable for this model is \f$k\f$, and the parameters of the model is \f$H\f$. +\f$\boldsymbol{\varepsilon}\f$, the output variable for this model is \f$k\f$, and the parameters of the model is \f$H\f$. ### Naming conventions {#naming-conventions} diff --git a/doc/content/system/model.md b/doc/content/system/model.md index 9c35b5eda7..c290a48a97 100644 --- a/doc/content/system/model.md +++ b/doc/content/system/model.md @@ -103,7 +103,7 @@ and `Model` \f$h\f$ only defines \f[ \frac{\partial y}{\partial y_1}, \quad \frac{\partial y}{\partial y_2}, \quad \frac{\partial y}{\partial x_4}. \f] -The assembly of the partial derivatives into the total derivative \f$\partial y / \partial \mathbf{x}\f$ using the chain rule is handled by NEML2. This design serves as the fundation for a modular model implementation: +The assembly of the partial derivatives into the total derivative \f$\partial y / \partial \boldsymbol{x}\f$ using the chain rule is handled by NEML2. This design serves as the fundation for a modular model implementation: - Each model _does not_ need to know its composition with others. - The same model partial derivatives can be reused in _any_ composition. diff --git a/include/neml2/models/solid_mechanics/ChabochePlasticHardening.h b/include/neml2/models/solid_mechanics/ChabochePlasticHardening.h index f68aeb479a..12f6d205ae 100644 --- a/include/neml2/models/solid_mechanics/ChabochePlasticHardening.h +++ b/include/neml2/models/solid_mechanics/ChabochePlasticHardening.h @@ -38,13 +38,13 @@ class ChabochePlasticHardening : public FlowRule protected: void set_value(bool out, bool dout_din, bool d2out_din2) override; - /// Backstress + /// Back stress const Variable & _X; /// Flow direction const Variable & _NM; - /// Rate of backstress + /// Rate of back stress Variable & _X_dot; const Scalar & _C; diff --git a/include/neml2/models/solid_mechanics/KinematicHardening.h b/include/neml2/models/solid_mechanics/KinematicHardening.h index 24d45e766c..ecc337bb51 100644 --- a/include/neml2/models/solid_mechanics/KinematicHardening.h +++ b/include/neml2/models/solid_mechanics/KinematicHardening.h @@ -39,7 +39,7 @@ class KinematicHardening : public Model /// Kinematic plastic strain const Variable & _Kp; - /// Backstress + /// Back stress Variable & _X; }; } // namespace neml2 diff --git a/include/neml2/models/solid_mechanics/OverStress.h b/include/neml2/models/solid_mechanics/OverStress.h index c8b0849e61..344308667d 100644 --- a/include/neml2/models/solid_mechanics/OverStress.h +++ b/include/neml2/models/solid_mechanics/OverStress.h @@ -41,7 +41,7 @@ class OverStress : public Model /// Mandel stress const Variable & _M; - /// Backstress + /// Back stress const Variable & _X; /// Overstress diff --git a/src/neml2/models/solid_mechanics/AssociativeIsotropicPlasticHardening.cxx b/src/neml2/models/solid_mechanics/AssociativeIsotropicPlasticHardening.cxx index 4b10c2db0c..860776c638 100644 --- a/src/neml2/models/solid_mechanics/AssociativeIsotropicPlasticHardening.cxx +++ b/src/neml2/models/solid_mechanics/AssociativeIsotropicPlasticHardening.cxx @@ -32,10 +32,21 @@ OptionSet AssociativeIsotropicPlasticHardening::expected_options() { OptionSet options = FlowRule::expected_options(); + options.doc() += " This object calculates the rate of equivalent plastic strain following " + "associative flow rule, i.e. \\f$ \\dot{\\varepsilon}_p = - \\dot{\\gamma} " + "\\frac{\\partial f}{\\partial k} \\f$, where \\f$ \\dot{\\varepsilon}_p \\f$ " + "is the equivalent plastic strain, \\f$ \\dot{\\gamma} \\f$ is the flow rate, " + "\\f$ f \\f$ is the yield function, and \\f$ k \\f$ is the isotropic hardening."; + options.set("isotropic_hardening_direction") = VariableName("state", "internal", "Nk"); + options.set("isotropic_hardening_direction").doc() = + "Direction of associative isotropic hardening which can be calculated using Normality."; + options.set("equivalent_plastic_strain_rate") = VariableName("state", "internal", "ep_rate"); + options.set("equivalent_plastic_strain_rate").doc() = "Rate of equivalent plastic strain"; + return options; } diff --git a/src/neml2/models/solid_mechanics/AssociativeKinematicPlasticHardening.cxx b/src/neml2/models/solid_mechanics/AssociativeKinematicPlasticHardening.cxx index d0835f6d79..92f93fafce 100644 --- a/src/neml2/models/solid_mechanics/AssociativeKinematicPlasticHardening.cxx +++ b/src/neml2/models/solid_mechanics/AssociativeKinematicPlasticHardening.cxx @@ -33,10 +33,22 @@ OptionSet AssociativeKinematicPlasticHardening::expected_options() { OptionSet options = FlowRule::expected_options(); + options.doc() += + " This object calculates the rate of kinematic plastic strain following associative flow " + "rule, i.e. \\f$ \\dot{\\boldsymbol{K}}_p = - \\dot{\\gamma} \\frac{\\partial f}{\\partial " + "\\boldsymbol{X}} \\f$, where \\f$ \\dot{\\boldsymbol{K}}_p \\f$ is the kinematic plastic " + "strain, \\f$ \\dot{\\gamma} \\f$ is the flow rate, \\f$ f \\f$ is the yield function, and " + "\\f$ \\boldsymbol{X} \\f$ is the kinematic hardening."; + options.set("kinematic_hardening_direction") = VariableName("state", "internal", "NX"); + options.set("kinematic_hardening_direction").doc() = + "Direction of associative kinematic hardening which can be calculated using Normality."; + options.set("kinematic_plastic_strain_rate") = VariableName("state", "internal", "Kp_rate"); + options.set("kinematic_plastic_strain_rate").doc() = "Rate of kinematic plastic strain"; + return options; } diff --git a/src/neml2/models/solid_mechanics/AssociativePlasticFlow.cxx b/src/neml2/models/solid_mechanics/AssociativePlasticFlow.cxx index 06c323001c..6b67a5addf 100644 --- a/src/neml2/models/solid_mechanics/AssociativePlasticFlow.cxx +++ b/src/neml2/models/solid_mechanics/AssociativePlasticFlow.cxx @@ -33,8 +33,19 @@ OptionSet AssociativePlasticFlow::expected_options() { OptionSet options = FlowRule::expected_options(); + options.doc() += + " This object calculates the rate of plastic strain following associative flow rule, i.e. " + "\\f$ \\dot{\\boldsymbol{\\varepsilon}}_p = - \\dot{\\gamma} \\frac{\\partial f}{\\partial " + "\\boldsymbol{M}} \\f$, where \\f$ \\dot{\\boldsymbol{\\varepsilon}}_p \\f$ is the plastic " + "strain, \\f$ \\dot{\\gamma} \\f$ is the flow rate, \\f$ f \\f$ is the yield function, and " + "\\f$ \\boldsymbol{M} \\f$ is the Mandel stress."; + options.set("flow_direction") = VariableName("state", "internal", "NM"); + options.set("flow_direction").doc() = "Flow direction which can be calculated using Normality"; + options.set("plastic_strain_rate") = VariableName("state", "internal", "Ep_rate"); + options.set("plastic_strain_rate").doc() = "Rate of plastic strain"; + return options; } diff --git a/src/neml2/models/solid_mechanics/ChabochePlasticHardening.cxx b/src/neml2/models/solid_mechanics/ChabochePlasticHardening.cxx index 5ffe8b0201..17809ce7f8 100644 --- a/src/neml2/models/solid_mechanics/ChabochePlasticHardening.cxx +++ b/src/neml2/models/solid_mechanics/ChabochePlasticHardening.cxx @@ -33,12 +33,34 @@ OptionSet ChabochePlasticHardening::expected_options() { OptionSet options = FlowRule::expected_options(); + options.doc() += + " This object defines the non-associative Chaboche kinematic hardening. In the Chaboche " + "model, back stress is directly treated as an internal variable. Rate of back stress is " + "given as \\f$ \\dot{\\boldsymbol{X}} = \\left( \\frac{2}{3} C \\frac{\\partial f}{\\partial " + "\\boldsymbol{M}} - g \\boldsymbol{X} \\right) \\dot{\\gamma} - A \\lVert \\boldsymbol{X} " + "\\rVert^{a - 1} \\boldsymbol{X} \\f$, including kinematic hardening, dynamic recovery, and " + "static recovery. \\f$ \\frac{\\partial f}{\\partial \\boldsymbol{M}} \\f$ is the flow " + "direction, \\f$ \\dot{\\gamma} \\f$ is the flow rate, and \\f$ C \\f$, \\f$ g \\f$, \\f$ A " + "\\f$, and \\f$ a \\f$ are material parameters."; + options.set("back_stress") = VariableName("state", "internal", "X"); + options.set("back_stress").doc() = "Back stress"; + options.set("flow_direction") = VariableName("state", "internal", "NM"); + options.set("flow_direction").doc() = "Flow direction"; + options.set>("C"); + options.set("C").doc() = "Kinematic hardening coefficient"; + options.set>("g"); + options.set("g").doc() = "Dynamic recovery coefficient"; + options.set>("A"); + options.set("A").doc() = "Static recovery prefactor"; + options.set>("a"); + options.set("a").doc() = "Static recovery exponent"; + return options; } diff --git a/src/neml2/models/solid_mechanics/Eigenstrain.cxx b/src/neml2/models/solid_mechanics/Eigenstrain.cxx index 85a3f4ddc5..c2133faf27 100644 --- a/src/neml2/models/solid_mechanics/Eigenstrain.cxx +++ b/src/neml2/models/solid_mechanics/Eigenstrain.cxx @@ -30,7 +30,10 @@ OptionSet Eigenstrain::expected_options() { OptionSet options = Model::expected_options(); + options.set("eigenstrain") = VariableName("forces", "Eg"); + options.set("eigenstrain").doc() = "Eigenstrain"; + return options; } diff --git a/src/neml2/models/solid_mechanics/ElasticStrain.cxx b/src/neml2/models/solid_mechanics/ElasticStrain.cxx index ac2ae95778..34ca23942a 100644 --- a/src/neml2/models/solid_mechanics/ElasticStrain.cxx +++ b/src/neml2/models/solid_mechanics/ElasticStrain.cxx @@ -33,10 +33,22 @@ OptionSet ElasticStrain::expected_options() { OptionSet options = Model::expected_options(); + options.doc() = "Calculate elastic strain given total and plastic strain (assuming additive " + "decomposition), i.e. \\f$ \\boldsymbol{\\varepsilon}_e = " + "\\boldsymbol{\\varepsilon} - \\boldsymbol{\\varepsilon}_p \\f$."; + options.set("total_strain") = VariableName("forces", "E"); + options.set("total_strain").doc() = "Total strain"; + options.set("plastic_strain") = VariableName("state", "internal", "Ep"); + options.set("plastic_strain").doc() = "Plastic strain"; + options.set("elastic_strain") = VariableName("state", "internal", "Ee"); + options.set("elastic_strain").doc() = "Elastic strain"; + options.set("rate_form") = false; + options.set("rate_form").doc() = "Whether the model defines the relationship in rate form"; + return options; } diff --git a/src/neml2/models/solid_mechanics/Elasticity.cxx b/src/neml2/models/solid_mechanics/Elasticity.cxx index 79ff8d2059..13e1e89f45 100644 --- a/src/neml2/models/solid_mechanics/Elasticity.cxx +++ b/src/neml2/models/solid_mechanics/Elasticity.cxx @@ -30,10 +30,24 @@ OptionSet Elasticity::expected_options() { OptionSet options = Model::expected_options(); + options.doc() = "Relate elastic strain to stress"; + options.set("strain") = VariableName("state", "internal", "Ee"); + options.set("strain").doc() = "Elastic strain"; + options.set("stress") = VariableName("state", "S"); + options.set("stress").doc() = "Stress"; + options.set("compliance") = false; + options.set("compliance").doc() = + "Whether the model defines the compliance relationship, i.e., mapping from stress to elastic " + "strain. When set to false (default), the model maps elastic strain to stress."; + options.set("rate_form") = false; + options.set("rate_form").doc() = + "Whether the model defines the stress-strain relationship in rate form. When set to true, " + "the model maps elastic strain *rate* to stress *rate*."; + return options; } diff --git a/src/neml2/models/solid_mechanics/FlowRule.cxx b/src/neml2/models/solid_mechanics/FlowRule.cxx index e428a62af5..ba11ae3a70 100644 --- a/src/neml2/models/solid_mechanics/FlowRule.cxx +++ b/src/neml2/models/solid_mechanics/FlowRule.cxx @@ -30,7 +30,12 @@ OptionSet FlowRule::expected_options() { OptionSet options = Model::expected_options(); + options.doc() = "Map the flow rate (i.e., the consistency parameter in the KKT conditions) to " + "the rate of internal variables."; + options.set("flow_rate") = VariableName("state", "internal", "gamma_rate"); + options.set("flow_rate").doc() = "Flow rate"; + return options; } diff --git a/src/neml2/models/solid_mechanics/GTNYieldFunction.cxx b/src/neml2/models/solid_mechanics/GTNYieldFunction.cxx index 51fa8d0f78..a3cadebc62 100644 --- a/src/neml2/models/solid_mechanics/GTNYieldFunction.cxx +++ b/src/neml2/models/solid_mechanics/GTNYieldFunction.cxx @@ -32,15 +32,44 @@ OptionSet GTNYieldFunction::expected_options() { OptionSet options = Model::expected_options(); + options.doc() = + "Gurson-Tvergaard-Needleman yield function for poroplasticity. The yield function is defined " + "as \\f$ f = \\left( \\frac{\\bar{\\sigma}}{\\sigma_y + k} \\right)^2 + 2 q_1 \\phi \\cosh " + "\\left( \\frac{1}{2} q_2 \\frac{3\\sigma_h-\\sigma_s}{\\sigma_y + k} \\right) - \\left( q_3 " + "\\phi^2 + 1 \\right) \\f$, where \\f$ \\bar{\\sigma} \\f$ is the von Mises stress, \\f$ " + "\\sigma_y \\f$ is the yield stress, \\f$ k \\f$ is isotropic hardening, \\f$ \\phi \\f$ is " + "the porosity, \\f$ \\sigma_h \\f$ is the hydrostatic stress, and \\f$ \\sigma_s \\f$ is the " + "void growth back stress (sintering stress). \\f$ q_1 \\f$, \\f$ q_2 \\f$, and \\f$ q_3 \\f$ " + "are parameters controlling the yield mechanisms."; + options.set>("yield_stress"); + options.set("yield_stress").doc() = "Yield stress"; + options.set>("q1"); + options.set("q1").doc() = + "Parameter controlling the balance/competition between plastic flow and void evolution."; + options.set>("q2"); + options.set("q2").doc() = "Void evolution rate"; + options.set>("q3"); + options.set("q3").doc() = "Pore pressure"; + options.set("flow_invariant") = VariableName("state", "internal", "se"); + options.set("flow_invariant").doc() = "Effective stress driving plastic flow"; + options.set("poro_invariant") = VariableName("state", "internal", "sp"); + options.set("poro_invariant").doc() = "Effective stress driving porous flow"; + options.set("isotropic_hardening"); + options.set("isotropic_hardening").doc() = "Isotropic hardening"; + options.set("void_fraction") = VariableName("state", "internal", "f"); + options.set("void_fraction").doc() = "Void fraction (porosity)"; + options.set("yield_function") = VariableName("state", "internal", "fp"); + options.set("yield_function").doc() = "Yield function"; + return options; } diff --git a/src/neml2/models/solid_mechanics/GursonCavitation.cxx b/src/neml2/models/solid_mechanics/GursonCavitation.cxx index 7f43498d31..41eee9edf8 100644 --- a/src/neml2/models/solid_mechanics/GursonCavitation.cxx +++ b/src/neml2/models/solid_mechanics/GursonCavitation.cxx @@ -33,9 +33,18 @@ OptionSet GursonCavitation::expected_options() { OptionSet options = Model::expected_options(); + options.doc() = "Local mass balance used in conjunction with the GTNYieldFunction, \\f$ " + "\\dot{\\phi} = (1-\\phi) \\dot{\\varepsilon}_p \\f$."; + options.set("plastic_strain_rate") = VariableName("state", "internal", "Ep_rate"); + options.set("plastic_strain_rate").doc() = "Plastic strain rate"; + options.set("void_fraction") = VariableName("state", "internal", "f"); + options.set("void_fraction").doc() = "Void fraction (porosity)"; + options.set("void_fraction_rate") = VariableName("state", "internal", "f_rate"); + options.set("void_fraction_rate").doc() = "Rate of void evolution"; + return options; } diff --git a/src/neml2/models/solid_mechanics/IsotropicHardening.cxx b/src/neml2/models/solid_mechanics/IsotropicHardening.cxx index a4420e8b9d..7ec4a11d7d 100644 --- a/src/neml2/models/solid_mechanics/IsotropicHardening.cxx +++ b/src/neml2/models/solid_mechanics/IsotropicHardening.cxx @@ -30,8 +30,14 @@ OptionSet IsotropicHardening::expected_options() { OptionSet options = Model::expected_options(); + options.doc() = "Map equivalent plastic strain to isotropic hardening"; + options.set("equivalent_plastic_strain") = VariableName("state", "internal", "ep"); + options.set("equivalent_plastic_strain").doc() = "Equivalent plastic strain"; + options.set("isotropic_hardening") = VariableName("state", "internal", "k"); + options.set("isotropic_hardening").doc() = "Isotropic hardening"; + return options; } diff --git a/src/neml2/models/solid_mechanics/IsotropicMandelStress.cxx b/src/neml2/models/solid_mechanics/IsotropicMandelStress.cxx index f3af34a02a..57882cfdb8 100644 --- a/src/neml2/models/solid_mechanics/IsotropicMandelStress.cxx +++ b/src/neml2/models/solid_mechanics/IsotropicMandelStress.cxx @@ -33,6 +33,9 @@ OptionSet IsotropicMandelStress::expected_options() { OptionSet options = MandelStress::expected_options(); + options.doc() += " For isotropic material under small deformation, the Mandel stress and the " + "Cauchy stress coincide."; + return options; } diff --git a/src/neml2/models/solid_mechanics/KinematicHardening.cxx b/src/neml2/models/solid_mechanics/KinematicHardening.cxx index 7a638a4040..7aabb7d79c 100644 --- a/src/neml2/models/solid_mechanics/KinematicHardening.cxx +++ b/src/neml2/models/solid_mechanics/KinematicHardening.cxx @@ -30,8 +30,14 @@ OptionSet KinematicHardening::expected_options() { OptionSet options = Model::expected_options(); + options.doc() = "Map kinematic plastic strain to back stress"; + options.set("kinematic_plastic_strain") = VariableName("state", "internal", "Kp"); + options.set("kinematic_plastic_strain").doc() = "Kinematic plastic strain"; + options.set("back_stress") = VariableName("state", "internal", "X"); + options.set("back_stress").doc() = "Back stress"; + return options; } diff --git a/src/neml2/models/solid_mechanics/LinearIsotropicElasticity.cxx b/src/neml2/models/solid_mechanics/LinearIsotropicElasticity.cxx index 23970803a0..cf0390ab80 100644 --- a/src/neml2/models/solid_mechanics/LinearIsotropicElasticity.cxx +++ b/src/neml2/models/solid_mechanics/LinearIsotropicElasticity.cxx @@ -33,8 +33,18 @@ OptionSet LinearIsotropicElasticity::expected_options() { OptionSet options = Elasticity::expected_options(); + options.doc() += " for linear isotropic material. \\f$ \\boldsymbol{\\sigma} = K \\tr " + "\\boldsymbol{\\varepsilon}_e + 2 G \\text{dev} \\boldsymbol{\\varepsilon}_e " + "\\f$, where \\f$ K \\f$ and \\f$ G \\f$ are bulk and shear moduli, " + "respectively. For convenience, this object only requests Young's modulus and " + "Poisson's ratio, and handles the Lame parameter conversion behind the scenes."; + options.set>("youngs_modulus"); + options.set("youngs_modulus").doc() = "Young's modulus"; + options.set>("poisson_ratio"); + options.set("poisson_ratio").doc() = "Poisson's ratio"; + return options; } diff --git a/src/neml2/models/solid_mechanics/LinearIsotropicHardening.cxx b/src/neml2/models/solid_mechanics/LinearIsotropicHardening.cxx index 0bd475f7de..7bfe7c6dd7 100644 --- a/src/neml2/models/solid_mechanics/LinearIsotropicHardening.cxx +++ b/src/neml2/models/solid_mechanics/LinearIsotropicHardening.cxx @@ -32,7 +32,12 @@ OptionSet LinearIsotropicHardening::expected_options() { OptionSet options = IsotropicHardening::expected_options(); + options.doc() += " following a linear relationship, i.e., \\f$ k = H \\varepsilon_p \\f$ where " + "\\f$ H \\f$ is the hardening modulus."; + options.set>("hardening_modulus"); + options.set("hardening_modulus").doc() = "Hardening modulus"; + return options; } diff --git a/src/neml2/models/solid_mechanics/LinearKinematicHardening.cxx b/src/neml2/models/solid_mechanics/LinearKinematicHardening.cxx index 640aa3d9a8..ece9cddaaa 100644 --- a/src/neml2/models/solid_mechanics/LinearKinematicHardening.cxx +++ b/src/neml2/models/solid_mechanics/LinearKinematicHardening.cxx @@ -33,7 +33,12 @@ OptionSet LinearKinematicHardening::expected_options() { OptionSet options = KinematicHardening::expected_options(); + options.doc() += " following a linear relationship, i.e., \\f$ \\boldsymbol{X} = H " + "\\boldsymbol{K}_p \\f$ where \\f$ H \\f$ is the hardening modulus."; + options.set>("hardening_modulus"); + options.set("hardening_modulus").doc() = "Hardening modulus"; + return options; } diff --git a/src/neml2/models/solid_mechanics/MandelStress.cxx b/src/neml2/models/solid_mechanics/MandelStress.cxx index 3d04f7d1f1..2af8167e7e 100644 --- a/src/neml2/models/solid_mechanics/MandelStress.cxx +++ b/src/neml2/models/solid_mechanics/MandelStress.cxx @@ -30,8 +30,14 @@ OptionSet MandelStress::expected_options() { OptionSet options = Model::expected_options(); + options.doc() = "Map Cauchy stress to Mandel stress"; + options.set("cauchy_stress") = VariableName("state", "S"); + options.set("cauchy_stress").doc() = "Cauchy stress"; + options.set("mandel_stress") = VariableName("state", "internal", "M"); + options.set("mandel_stress").doc() = "Mandel stress"; + return options; } diff --git a/src/neml2/models/solid_mechanics/Normality.cxx b/src/neml2/models/solid_mechanics/Normality.cxx index 44ad2db8a7..c8922f7025 100644 --- a/src/neml2/models/solid_mechanics/Normality.cxx +++ b/src/neml2/models/solid_mechanics/Normality.cxx @@ -32,10 +32,21 @@ OptionSet Normality::expected_options() { OptionSet options = Model::expected_options(); + options.doc() = "Store the first derivatives of a scalar-valued function in given variables, " + "i.e. \\f$ u_i = \\dfrac{f(\\boldsymbol{v})}{v_i} \\f$."; + options.set("model"); + options.set("model").doc() = "The model which evaluates the scalar-valued function"; + options.set("function"); + options.set("function").doc() = "Function to take derivative"; + options.set>("from"); + options.set("from").doc() = "Function arguments to take derivatives w.r.t."; + options.set>("to"); + options.set("to").doc() = "Variables to store the first derivatives"; + return options; } diff --git a/src/neml2/models/solid_mechanics/OlevskySinteringStress.cxx b/src/neml2/models/solid_mechanics/OlevskySinteringStress.cxx index 368265a5d1..6ddea100e6 100644 --- a/src/neml2/models/solid_mechanics/OlevskySinteringStress.cxx +++ b/src/neml2/models/solid_mechanics/OlevskySinteringStress.cxx @@ -32,10 +32,24 @@ OptionSet OlevskySinteringStress::expected_options() { OptionSet options = Model::expected_options(); + options.doc() = "Define the Olevsky-Skorohod sintering stress to be used in conjunction with " + "poroplasticity yield functions such as the GTNYieldFunction. The sintering " + "stress is defined as \\f$ \\sigma_s = 3 \\dfrac{\\gamma}{r} \\phi^2 \\f$, where " + "\\f$ \\gamma \\f$ is the surface tension, \\f$ r \\f$ is the size of the " + "particles/powders, and \\f$ \\phi \\f$ is the void fraction."; + options.set("sintering_stress") = VariableName("state", "internal", "ss"); + options.set("sintering_stress").doc() = "Sintering stress"; + options.set("void_fraction") = VariableName("state", "internal", "f"); + options.set("void_fraction").doc() = "Void fraction"; + options.set>("surface_tension"); + options.set("surface_tension").doc() = "Surface tension"; + options.set>("particle_radius"); + options.set("particle_radius").doc() = "Particle radius"; + return options; } diff --git a/src/neml2/models/solid_mechanics/OverStress.cxx b/src/neml2/models/solid_mechanics/OverStress.cxx index 6b94d37d0a..672b5b190d 100644 --- a/src/neml2/models/solid_mechanics/OverStress.cxx +++ b/src/neml2/models/solid_mechanics/OverStress.cxx @@ -33,9 +33,19 @@ OptionSet OverStress::expected_options() { OptionSet options = Model::expected_options(); + options.doc() = "Calculate the over stress \\f$ \\boldsymbol{O} = \\boldsymbol{M} - " + "\\boldsymbol{X} \\f$, where \\f$ \\boldsymbol{M} \\f$ is the Mandel stress and " + "\\f$ \\boldsymbol{X} \\f$ is the back stress."; + options.set("mandel_stress") = VariableName("state", "internal", "M"); + options.set("mandel_stress").doc() = "Mandel stress"; + options.set("back_stress") = VariableName("state", "internal", "X"); + options.set("back_stress").doc() = "Back stress"; + options.set("over_stress") = VariableName("state", "internal", "O"); + options.set("over_stress").doc() = "Over stress"; + return options; } diff --git a/src/neml2/models/solid_mechanics/PerzynaPlasticFlowRate.cxx b/src/neml2/models/solid_mechanics/PerzynaPlasticFlowRate.cxx index 44ee98690e..90ca2642ad 100644 --- a/src/neml2/models/solid_mechanics/PerzynaPlasticFlowRate.cxx +++ b/src/neml2/models/solid_mechanics/PerzynaPlasticFlowRate.cxx @@ -32,8 +32,18 @@ OptionSet PerzynaPlasticFlowRate::expected_options() { OptionSet options = PlasticFlowRate::expected_options(); + options.doc() = + "Perzyna's viscous approximation of the consistent yield envelope (with a power " + "law), i.e. \\f$ \\dot{\\gamma} = \\left( \\frac{\\left< f \\right>}{\\eta} \\right)^n \\f$, " + "where \\f$ f \\f$ is the yield function, \\f$ \\eta \\f$ is the reference stress, and \\f$ " + "n \\f$ is the power-law exponent."; + options.set>("reference_stress"); + options.set("reference_stress").doc() = "Reference stress"; + options.set>("exponent"); + options.set("exponent").doc() = "Power-law exponent"; + return options; } diff --git a/src/neml2/models/solid_mechanics/PlasticFlowRate.cxx b/src/neml2/models/solid_mechanics/PlasticFlowRate.cxx index 587304797e..dffb3be995 100644 --- a/src/neml2/models/solid_mechanics/PlasticFlowRate.cxx +++ b/src/neml2/models/solid_mechanics/PlasticFlowRate.cxx @@ -30,8 +30,13 @@ OptionSet PlasticFlowRate::expected_options() { OptionSet options = Model::expected_options(); + options.set("yield_function") = VariableName("state", "internal", "fp"); + options.set("yield_function").doc() = "Yield function"; + options.set("flow_rate") = VariableName("state", "internal", "gamma_rate"); + options.set("flow_rate").doc() = "Flow rate"; + return options; } diff --git a/src/neml2/models/solid_mechanics/RateIndependentPlasticFlowConstraint.cxx b/src/neml2/models/solid_mechanics/RateIndependentPlasticFlowConstraint.cxx index b78ceba82f..29c38c298e 100644 --- a/src/neml2/models/solid_mechanics/RateIndependentPlasticFlowConstraint.cxx +++ b/src/neml2/models/solid_mechanics/RateIndependentPlasticFlowConstraint.cxx @@ -32,9 +32,21 @@ OptionSet RateIndependentPlasticFlowConstraint::expected_options() { OptionSet options = Model::expected_options(); + options.doc() = "Solve the consistent plasticity yield envelope by solving the equivalent " + "complementarity condition \\f[ r = \\begin{cases} \\dot{\\gamma}, & f < 0 " + "\\\\ f, & f \\geq 0. \\end{cases} \\f]"; + options.set("yield_function") = VariableName("state", "internal", "fp"); + options.set("yield_function").doc() = "Yield function"; + options.set("flow_rate") = VariableName("state", "internal", "gamma_rate"); + options.set("flow_rate").doc() = "Flow rate"; + options.set("yielding_tolerance") = 1e-8; + options.set("yielding_tolerance").doc() = + "Tolerance for determining whether the current material state is inside or outside the yield " + "envelope"; + return options; } diff --git a/src/neml2/models/solid_mechanics/ThermalEigenstrain.cxx b/src/neml2/models/solid_mechanics/ThermalEigenstrain.cxx index 9bc005809b..82985dd136 100644 --- a/src/neml2/models/solid_mechanics/ThermalEigenstrain.cxx +++ b/src/neml2/models/solid_mechanics/ThermalEigenstrain.cxx @@ -32,9 +32,21 @@ OptionSet ThermalEigenstrain::expected_options() { OptionSet options = Eigenstrain::expected_options(); + options.doc() = + "Define the (cummulative, as opposed to instantaneous) linear isotropic thermal eigenstrain, " + "i.e. \\f$ \\boldsymbol{\\varepsilon}_T = \\alpha (T - T_0) \\boldsymbol{I} \\f$, where \\f$ " + "\\alpha \\f$ is the coefficient of thermal expansion (CTE), \\f$ T \\f$ is the temperature, " + "and \\f$ T_0 \\f$ is the reference (stress-free) temperature."; + options.set("temperature") = VariableName("forces", "T"); + options.set("temperature").doc() = "Temperature"; + options.set>("reference_temperature"); + options.set("reference_temperature").doc() = "Reference (stress-free) temperature"; + options.set>("CTE"); + options.set("CTE").doc() = "Coefficient of thermal expansion"; + return options; } diff --git a/src/neml2/models/solid_mechanics/TotalStrain.cxx b/src/neml2/models/solid_mechanics/TotalStrain.cxx index eccf7b2458..c5843de848 100644 --- a/src/neml2/models/solid_mechanics/TotalStrain.cxx +++ b/src/neml2/models/solid_mechanics/TotalStrain.cxx @@ -33,10 +33,20 @@ OptionSet TotalStrain::expected_options() { OptionSet options = Model::expected_options(); + options.doc() = "Calculate the total strain by summing the elastic and plastic strain."; + options.set("elastic_strain") = VariableName("state", "internal", "Ee"); + options.set("elastic_strain").doc() = "Elastic strain"; + options.set("plastic_strain") = VariableName("state", "internal", "Ep"); + options.set("plastic_strain").doc() = "Plastic strain"; + options.set("total_strain") = VariableName("state", "E"); + options.set("total_strain").doc() = "Total strain"; + options.set("rate_form") = false; + options.set("rate_form").doc() = "Whether to define the relationship in rate form"; + return options; } diff --git a/src/neml2/models/solid_mechanics/VoceIsotropicHardening.cxx b/src/neml2/models/solid_mechanics/VoceIsotropicHardening.cxx index 610d6a9628..a44952c306 100644 --- a/src/neml2/models/solid_mechanics/VoceIsotropicHardening.cxx +++ b/src/neml2/models/solid_mechanics/VoceIsotropicHardening.cxx @@ -32,8 +32,16 @@ OptionSet VoceIsotropicHardening::expected_options() { OptionSet options = IsotropicHardening::expected_options(); + options.doc() = "Voce isotropic hardening model, \\f$ h = R \\left[ 1 - \\exp(-d \\varepsilon_p) " + "\\right] \\f$, where \\f$ R \\f$ is the isotropic hardening upon saturation, " + "and \\f$ d \\f$ is the hardening rate."; + options.set>("saturated_hardening"); + options.set("saturated_hardening").doc() = "Saturated isotropic hardening"; + options.set>("saturation_rate"); + options.set("saturation_rate").doc() = "Hardening saturation rate"; + return options; } diff --git a/src/neml2/models/solid_mechanics/YieldFunction.cxx b/src/neml2/models/solid_mechanics/YieldFunction.cxx index 2f6e1ea4bc..275fdbc589 100644 --- a/src/neml2/models/solid_mechanics/YieldFunction.cxx +++ b/src/neml2/models/solid_mechanics/YieldFunction.cxx @@ -32,10 +32,23 @@ OptionSet YieldFunction::expected_options() { OptionSet options = Model::expected_options(); + options.doc() = + "Classical macroscale plasticity yield function, \\f$ f = \\bar{\\sigma} - \\sigma_y - h " + "\\f$, where \\f$ \\bar{\\sigma} \\f$ is the effective stress, \\f$ \\sigma_y \\f$ is the " + "yield stress, and \\f$ h \\f$ is the isotropic hardening."; + options.set>("yield_stress"); + options.set("yield_stress").doc() = "Yield stress"; + options.set("effective_stress") = VariableName("state", "internal", "s"); + options.set("effective_stress").doc() = "Effective stress"; + options.set("isotropic_hardening"); + options.set("isotropic_hardening").doc() = "Isotropic hardening"; + options.set("yield_function") = VariableName("state", "internal", "fp"); + options.set("yield_function").doc() = "Yield function"; + return options; } diff --git a/src/neml2/solvers/Newton.cxx b/src/neml2/solvers/Newton.cxx index 1595d1cc49..6083cb6390 100644 --- a/src/neml2/solvers/Newton.cxx +++ b/src/neml2/solvers/Newton.cxx @@ -34,6 +34,8 @@ OptionSet Newton::expected_options() { OptionSet options = NonlinearSolver::expected_options(); + options.doc() = "The standard Newton-Raphson solver which always takes the 'full' Newton step."; + return options; } diff --git a/src/neml2/solvers/NewtonWithLineSearch.cxx b/src/neml2/solvers/NewtonWithLineSearch.cxx index 9485d93e8e..513209febf 100644 --- a/src/neml2/solvers/NewtonWithLineSearch.cxx +++ b/src/neml2/solvers/NewtonWithLineSearch.cxx @@ -34,9 +34,21 @@ OptionSet NewtonWithLineSearch::expected_options() { OptionSet options = Newton::expected_options(); + options.doc() = "The Newton-Raphson solver with line search."; + options.set("max_linesearch_iterations") = 10; + options.set("max_linesearch_iterations").doc() = + "Maximum allowable linesearch iterations. No error is produced upon reaching the maximum " + "number of iterations, and the scale factor in the last iteration is used to scale the step."; + options.set("linesearch_cutback") = 2.0; + options.set("linesearch_cutback").doc() = "Linesearch cut-back factor when the current scale " + "factor cannot sufficiently reduce the residual."; + options.set("linesearch_stopping_criteria") = 1.0e-3; + options.set("linesearch_stopping_criteria").doc() = + "The lineseach tolerance slightly relaxing the definition of residual decrease"; + return options; } diff --git a/src/neml2/solvers/NewtonWithTrustRegion.cxx b/src/neml2/solvers/NewtonWithTrustRegion.cxx index e724ececa6..919777074d 100644 --- a/src/neml2/solvers/NewtonWithTrustRegion.cxx +++ b/src/neml2/solvers/NewtonWithTrustRegion.cxx @@ -34,16 +34,45 @@ OptionSet NewtonWithTrustRegion::expected_options() { OptionSet options = Newton::expected_options(); + options.doc() = + "A trust-region Newton solver. The step size and direction are modified by solving a " + "constrained minimization problem using the local quadratic approximation. The default " + "solver parameters are chosen based on a limited set of problems we tested on and are " + "expected to be tuned."; + options.set("delta_0") = 1.0; + options.set("delta_0").doc() = "Initial trust region radius"; + options.set("delta_max") = 10.0; + options.set("delta_max").doc() = "Maximum trust region radius"; + options.set("reduce_criteria") = 0.25; + options.set("reduce_criteria").doc() = "The trust region radius is reduced when the merit " + "function reduction is below this threshold"; + options.set("expand_criteria") = 0.75; + options.set("expand_criteria").doc() = "The trust region radius is increased when the merit " + "function reduction is above this threshold"; + options.set("reduce_factor") = 0.25; + options.set("reduce_factor").doc() = "Factor to apply when reducing the trust region radius"; + options.set("expand_factor") = 2.0; + options.set("expand_factor").doc() = "Factor to apply when increasing the trust region radius"; + options.set("accept_criteria") = 0.1; + options.set("accept_criteria").doc() = + "Reject the current step when the merit function reduction is below this threshold"; + options.set("subproblem_rel_tol") = 1e-6; + options.set("subproblem_rel_tol").doc() = "Relative tolerance used for the quadratic sub-problem"; + options.set("subproblem_abs_tol") = 1e-8; + options.set("subproblem_rel_tol").doc() = "Absolute tolerance used for the quadratic sub-problem"; + options.set("subproblem_max_its") = 10; + options.set("subproblem_max_its").doc() = + "Maximum number of allowable iterations when solving the quadratic sub-problem"; return options; } diff --git a/src/neml2/solvers/NonlinearSolver.cxx b/src/neml2/solvers/NonlinearSolver.cxx index d6ffe59f98..22885e0f79 100644 --- a/src/neml2/solvers/NonlinearSolver.cxx +++ b/src/neml2/solvers/NonlinearSolver.cxx @@ -30,9 +30,17 @@ OptionSet NonlinearSolver::expected_options() { OptionSet options = Solver::expected_options(); + options.set("abs_tol") = 1e-10; + options.set("abs_tol").doc() = "Absolute tolerance in the convergence criteria"; + options.set("rel_tol") = 1e-8; + options.set("rel_tol").doc() = "Relative tolerance in the convergence criteria"; + options.set("max_its") = 100; + options.set("max_its").doc() = + "Maximum number of iterations allowed before issuing an error/exception"; + return options; } diff --git a/src/neml2/solvers/NonlinearSystem.cxx b/src/neml2/solvers/NonlinearSystem.cxx index b7da978667..badbaaddad 100644 --- a/src/neml2/solvers/NonlinearSystem.cxx +++ b/src/neml2/solvers/NonlinearSystem.cxx @@ -31,9 +31,22 @@ OptionSet NonlinearSystem::expected_options() { OptionSet options; + options.set("automatic_scaling") = false; + options.set("automatic_scaling").doc() = + "Whether to perform automatic scaling. See neml2::NonlinearSystem::init_scaling for " + "implementation details."; + options.set("automatic_scaling_tol") = 0.01; + options.set("automatic_scaling_tol").doc() = + "Tolerance used in iteratively updating the scaling matrices."; + options.set("automatic_scaling_miter") = 20; + options.set("automatic_scaling_miter").doc() = + "Maximum number of automatic scaling iterations. No error is produced upon reaching the " + "maximum number of scaling iterations, and the scaling matrices obtained at the last " + "iteration are used to scale the nonlinear system."; + return options; } diff --git a/src/neml2/solvers/Solver.cxx b/src/neml2/solvers/Solver.cxx index 835c05a302..b316b0f61e 100644 --- a/src/neml2/solvers/Solver.cxx +++ b/src/neml2/solvers/Solver.cxx @@ -31,7 +31,10 @@ Solver::expected_options() { OptionSet options = NEML2Object::expected_options(); options.section() = "Solvers"; + options.set("verbose"); + options.set("verbose").doc() = "Verbosity of the solver"; + return options; } From ccbd15dfed663fe020290d2210578ff29980414e Mon Sep 17 00:00:00 2001 From: Gary Hu Date: Thu, 13 Jun 2024 14:50:18 -0500 Subject: [PATCH 49/55] Document all drivers --- ...formationIncrementalSolidMechanicsDriver.h | 11 +++-- runner/benchmark/taylor_rolling_fcc/model.i | 1 - .../taylor_single_orientation/model.i | 1 - src/neml2/base/OptionSet.cxx | 16 ++++++- src/neml2/drivers/Driver.cxx | 3 ++ src/neml2/drivers/TransientDriver.cxx | 43 +++++++++++++++++++ ...rmationIncrementalSolidMechanicsDriver.cxx | 35 +++++++++++---- .../solid_mechanics/SolidMechanicsDriver.cxx | 18 ++++++++ .../single_crystal_coupled/model.i | 1 - .../single_crystal_decoupled/model.i | 1 - .../model.i | 1 - .../solid_mechanics/cp_basic/cp_basic.i | 1 - .../solid_mechanics/cp_taylor/cp_taylor.i | 1 - 13 files changed, 111 insertions(+), 22 deletions(-) diff --git a/include/neml2/drivers/solid_mechanics/LargeDeformationIncrementalSolidMechanicsDriver.h b/include/neml2/drivers/solid_mechanics/LargeDeformationIncrementalSolidMechanicsDriver.h index 0e1bef9294..86d1dba98b 100644 --- a/include/neml2/drivers/solid_mechanics/LargeDeformationIncrementalSolidMechanicsDriver.h +++ b/include/neml2/drivers/solid_mechanics/LargeDeformationIncrementalSolidMechanicsDriver.h @@ -71,13 +71,16 @@ class LargeDeformationIncrementalSolidMechanicsDriver : public TransientDriver VariableName _driving_force_name; /** - * The value of the (total) vorticity + * The name of the total vorticity */ - WR2 _vorticity; + VariableName _vorticity_name; + + /// Whether vorticity is prescribed + const bool _vorticity_prescribed; /** - * The name of the total vorticity + * The value of the (total) vorticity */ - VariableName _vorticity_name; + WR2 _vorticity; }; } diff --git a/runner/benchmark/taylor_rolling_fcc/model.i b/runner/benchmark/taylor_rolling_fcc/model.i index 1f2552ae68..573fc78a1e 100644 --- a/runner/benchmark/taylor_rolling_fcc/model.i +++ b/runner/benchmark/taylor_rolling_fcc/model.i @@ -89,7 +89,6 @@ times = 'times' prescribed_deformation_rate = 'deformation_rate' prescribed_vorticity = 'vorticity' - provide_vorticity = true ic_rot_names = 'state/orientation' ic_rot_values = 'initial_orientation' predictor = 'CP_PREVIOUS_STATE' diff --git a/runner/benchmark/taylor_single_orientation/model.i b/runner/benchmark/taylor_single_orientation/model.i index a843abd829..a29fffd0ad 100644 --- a/runner/benchmark/taylor_single_orientation/model.i +++ b/runner/benchmark/taylor_single_orientation/model.i @@ -88,7 +88,6 @@ times = 'times' prescribed_deformation_rate = 'deformation_rate' prescribed_vorticity = 'vorticity' - provide_vorticity = true ic_rot_names = 'state/orientation' ic_rot_values = 'initial_orientation' predictor = 'CP_PREVIOUS_STATE' diff --git a/src/neml2/base/OptionSet.cxx b/src/neml2/base/OptionSet.cxx index 499f9993a7..6b8572d1de 100644 --- a/src/neml2/base/OptionSet.cxx +++ b/src/neml2/base/OptionSet.cxx @@ -90,13 +90,25 @@ OptionSet::print(std::ostream & os) const OptionSet::const_iterator it = _values.begin(); os << " section: " << section() << '\n'; - os << " doc: " << doc() << '\n'; + if (doc().empty()) + os << " doc:\n"; + else + { + os << " doc: |-\n"; + os << " " << doc() << '\n'; + } while (it != _values.end()) { os << " " << it->first << ":\n"; os << " type: " << it->second->type() << '\n'; - os << " doc: " << it->second->doc() << '\n'; + if (it->second->doc().empty()) + os << " doc:\n"; + else + { + os << " doc: |-\n"; + os << " " << it->second->doc() << '\n'; + } os << " suppressed: " << it->second->suppressed() << '\n'; os << " value: "; it->second->print(os); diff --git a/src/neml2/drivers/Driver.cxx b/src/neml2/drivers/Driver.cxx index 36bed0ecca..3a1c6b0964 100644 --- a/src/neml2/drivers/Driver.cxx +++ b/src/neml2/drivers/Driver.cxx @@ -31,7 +31,10 @@ Driver::expected_options() { OptionSet options = NEML2Object::expected_options(); options.section() = "Drivers"; + options.set("verbose") = false; + options.set("verbose").doc() = "Whether to output additional logging information"; + return options; } diff --git a/src/neml2/drivers/TransientDriver.cxx b/src/neml2/drivers/TransientDriver.cxx index 050738f8cc..22ee9f5a38 100644 --- a/src/neml2/drivers/TransientDriver.cxx +++ b/src/neml2/drivers/TransientDriver.cxx @@ -34,23 +34,66 @@ OptionSet TransientDriver::expected_options() { OptionSet options = Driver::expected_options(); + options.set("model"); + options.set("model").doc() = "The material model to be updated by the driver"; + options.set>("times"); + options.set("times").doc() = + "Time steps to perform the material update. The times tensor must have exactly 2 dimensions. " + "The first dimension represents time steps, and the second dimension represents batches " + "(i.e., how many material models to update simultaneously)."; + options.set("time") = VariableName("forces", "t"); + options.set("time").doc() = "Time"; + options.set("predictor") = "PREVIOUS_STATE"; + options.set("predictor").doc() = + "Predictor used to set the initial guess for each time step. Options are PREVIOUS_STATE, " + "LINEAR_EXTRAPOLATION, CP_PREVIOUS_STATE, and CP_LINEAR_EXTRAPOLATION. The options prefixed " + "with 'CP_' are specifically designed for crystal plasticity models."; + options.set("cp_elastic_scale") = 1.0; + options.set("cp_elastic_scale").doc() = "Elastic step scale factor used in the 'CP_' predictors"; + options.set("save_as"); + options.set("save_as").doc() = + "File path (absolute or relative to the working directory) to store the results"; + options.set("show_parameters") = false; + options.set("show_parameters").doc() = "Whether to show model parameters at the beginning"; + options.set("show_input_axis") = false; + options.set("show_input_axis").doc() = "Whether to show model input axis at the beginning"; + options.set("show_output_axis") = false; + options.set("show_output_axis").doc() = "Whether to show model output axis at the beginning"; + options.set("device") = "cpu"; + options.set("device").doc() = + "Device on which to evaluate the material model. The string supplied must follow the " + "following schema: (cpu|cuda)[:] where cpu or cuda specifies the device type, " + "and : optionally specifies a device index. For example, device='cpu' sets the " + "target compute device to be CPU, and device='cuda:1' sets the target compute device to be " + "CUDA with device ID 1."; options.set>("ic_scalar_names"); + options.set("ic_scalar_names").doc() = "Apply initial conditions to these Scalar variables"; + options.set>>("ic_scalar_values"); + options.set("ic_scalar_values").doc() = "Initial condition values for the Scalar variables"; + options.set>("ic_rot_names"); + options.set("ic_rot_names").doc() = "Apply initial conditions to these Rot variables"; + options.set>>("ic_rot_values"); + options.set("ic_rot_values").doc() = "Initial condition values for the Rot variables"; + options.set>("ic_sr2_names"); + options.set("ic_sr2_names").doc() = "Apply initial conditions to these SR2 variables"; + options.set>>("ic_sr2_values"); + options.set("ic_sr2_values").doc() = "Initial condition values for the SR2 variables"; return options; } diff --git a/src/neml2/drivers/solid_mechanics/LargeDeformationIncrementalSolidMechanicsDriver.cxx b/src/neml2/drivers/solid_mechanics/LargeDeformationIncrementalSolidMechanicsDriver.cxx index b2282b4529..0506094d9d 100644 --- a/src/neml2/drivers/solid_mechanics/LargeDeformationIncrementalSolidMechanicsDriver.cxx +++ b/src/neml2/drivers/solid_mechanics/LargeDeformationIncrementalSolidMechanicsDriver.cxx @@ -32,15 +32,32 @@ OptionSet LargeDeformationIncrementalSolidMechanicsDriver::expected_options() { OptionSet options = TransientDriver::expected_options(); + options.doc() = "Driver for large deformation solid mechanics material model. The material model " + "is updated *incrementally*."; + options.set("control") = "STRAIN"; + options.set("control").doc() = "External control of the material update. Options are STRAIN and " + "STRESS, for strain control and stress control, respectively."; + options.set("deformation_rate") = VariableName("forces", "deformation_rate"); + options.set("deformation_rate").doc() = "Deformation rate"; + options.set("cauchy_stress_rate") = VariableName("forces", "cauchy_stress_rate"); + options.set("cauchy_stress_rate").doc() = "Cauchy stress rate"; + + options.set("vorticity") = VariableName("forces", "vorticity"); + options.set("vorticity").doc() = "Vorticity"; + options.set>("prescribed_deformation_rate"); + options.set("prescribed_deformation_rate").doc() = + "Prescribed deformation rate (when control = STRAIN)"; + options.set>("prescribed_cauchy_stress_rate"); + options.set("prescribed_cauchy_stress_rate").doc() = + "Prescribed cauchy stress rate (when control = STRESS)"; - options.set("vorticity") = VariableName("forces", "vorticity"); - options.set("provide_vorticity") = false; - options.set>("prescribed_vorticity") = "vorticity"; + options.set>("prescribed_vorticity"); + options.set("prescribed_vorticity").doc() = "Prescribed vorticity"; return options; } @@ -49,7 +66,12 @@ LargeDeformationIncrementalSolidMechanicsDriver::LargeDeformationIncrementalSoli const OptionSet & options) : TransientDriver(options), _control(options.get("control")), - _vorticity_name(options.get("vorticity")) + _vorticity_name(options.get("vorticity")), + _vorticity_prescribed( + !options.get>("prescribed_vorticity").raw().empty()), + _vorticity(_vorticity_prescribed + ? WR2(options.get>("prescribed_vorticity")) + : WR2::zeros(_driving_force.batch_sizes())) { if (_control == "STRAIN") { @@ -66,11 +88,6 @@ LargeDeformationIncrementalSolidMechanicsDriver::LargeDeformationIncrementalSoli throw NEMLException("Unsupported control type."); // LCOV_EXCL_STOP - if (options.get("provide_vorticity")) - _vorticity = WR2(options.get>("prescribed_vorticity")); - else - _vorticity = WR2::zeros(_driving_force.batch_sizes()); - _driving_force = _driving_force.to(_device); _vorticity = _vorticity.to(_device); diff --git a/src/neml2/drivers/solid_mechanics/SolidMechanicsDriver.cxx b/src/neml2/drivers/solid_mechanics/SolidMechanicsDriver.cxx index 8fce1a8c01..e0b08096de 100644 --- a/src/neml2/drivers/solid_mechanics/SolidMechanicsDriver.cxx +++ b/src/neml2/drivers/solid_mechanics/SolidMechanicsDriver.cxx @@ -32,13 +32,31 @@ OptionSet SolidMechanicsDriver::expected_options() { OptionSet options = TransientDriver::expected_options(); + options.doc() = + "Driver for small deformation solid mechanics material model with optional thermal coupling."; + options.set("control") = "STRAIN"; + options.set("control").doc() = "External control of the material update. Options are STRAIN and " + "STRESS, for strain control and stress control, respectively."; + options.set("total_strain") = VariableName("forces", "E"); + options.set("total_strain").doc() = "Total strain"; + options.set("cauchy_stress") = VariableName("forces", "S"); + options.set("total_strain").doc() = "Cauchy stress"; + options.set("temperature") = VariableName("forces", "T"); + options.set("total_strain").doc() = "Temperature"; + options.set>("prescribed_strains"); + options.set("prescribed_strains").doc() = "Prescribed strain (when control = STRAIN)"; + options.set>("prescribed_stresses"); + options.set("prescribed_stresses").doc() = "Prescribed stress (when control = STRESS)"; + options.set>("prescribed_temperatures"); + options.set("prescribed_temperatures").doc() = "Prescribed temperature"; + return options; } diff --git a/tests/regression/solid_mechanics/crystal_plasticity/single_crystal_coupled/model.i b/tests/regression/solid_mechanics/crystal_plasticity/single_crystal_coupled/model.i index 74e47df2d2..6bf0ceb771 100644 --- a/tests/regression/solid_mechanics/crystal_plasticity/single_crystal_coupled/model.i +++ b/tests/regression/solid_mechanics/crystal_plasticity/single_crystal_coupled/model.i @@ -109,7 +109,6 @@ times = 'times' prescribed_deformation_rate = 'deformation_rate' prescribed_vorticity = 'vorticity' - provide_vorticity = true ic_rot_names = 'state/orientation' ic_rot_values = 'initial_orientation' predictor = 'CP_PREVIOUS_STATE' diff --git a/tests/regression/solid_mechanics/crystal_plasticity/single_crystal_decoupled/model.i b/tests/regression/solid_mechanics/crystal_plasticity/single_crystal_decoupled/model.i index fc7fdbe58d..5b176852b7 100644 --- a/tests/regression/solid_mechanics/crystal_plasticity/single_crystal_decoupled/model.i +++ b/tests/regression/solid_mechanics/crystal_plasticity/single_crystal_decoupled/model.i @@ -109,7 +109,6 @@ times = 'times' prescribed_deformation_rate = 'deformation_rate' prescribed_vorticity = 'vorticity' - provide_vorticity = true ic_rot_names = 'state/orientation' ic_rot_values = 'initial_orientation' predictor = 'CP_PREVIOUS_STATE' diff --git a/tests/regression/solid_mechanics/crystal_plasticity/single_crystal_decoupled_explicit_orientation/model.i b/tests/regression/solid_mechanics/crystal_plasticity/single_crystal_decoupled_explicit_orientation/model.i index 4eec4cfadd..543f5e0477 100644 --- a/tests/regression/solid_mechanics/crystal_plasticity/single_crystal_decoupled_explicit_orientation/model.i +++ b/tests/regression/solid_mechanics/crystal_plasticity/single_crystal_decoupled_explicit_orientation/model.i @@ -109,7 +109,6 @@ times = 'times' prescribed_deformation_rate = 'deformation_rate' prescribed_vorticity = 'vorticity' - provide_vorticity = true ic_rot_names = 'state/orientation' ic_rot_values = 'initial_orientation' predictor = 'CP_PREVIOUS_STATE' diff --git a/tests/verification/solid_mechanics/cp_basic/cp_basic.i b/tests/verification/solid_mechanics/cp_basic/cp_basic.i index a14adfd9ec..bb1e3a9ded 100644 --- a/tests/verification/solid_mechanics/cp_basic/cp_basic.i +++ b/tests/verification/solid_mechanics/cp_basic/cp_basic.i @@ -48,7 +48,6 @@ times = 'times' prescribed_deformation_rate = 'deformation_rate' prescribed_vorticity = 'vorticity' - provide_vorticity = true ic_rot_names = 'state/orientation' ic_rot_values = 'initial_orientation' predictor = 'CP_PREVIOUS_STATE' diff --git a/tests/verification/solid_mechanics/cp_taylor/cp_taylor.i b/tests/verification/solid_mechanics/cp_taylor/cp_taylor.i index 4dbe00904c..86bf767bc3 100644 --- a/tests/verification/solid_mechanics/cp_taylor/cp_taylor.i +++ b/tests/verification/solid_mechanics/cp_taylor/cp_taylor.i @@ -58,7 +58,6 @@ times = 'times' prescribed_deformation_rate = 'deformation_rate' prescribed_vorticity = 'vorticity' - provide_vorticity = true ic_rot_names = 'state/orientation' ic_rot_values = 'initial_orientation' predictor = 'CP_PREVIOUS_STATE' From 48345c206e88388c89e8cfae429a8f662e1d834d Mon Sep 17 00:00:00 2001 From: Gary Hu Date: Thu, 13 Jun 2024 15:01:35 -0500 Subject: [PATCH 50/55] Log missing syntax doc --- doc/CMakeLists.txt | 2 +- scripts/syntax_to_md.py | 111 +++++++++++++++++++++++----------------- 2 files changed, 65 insertions(+), 48 deletions(-) diff --git a/doc/CMakeLists.txt b/doc/CMakeLists.txt index bd91762416..c9d96bdc54 100644 --- a/doc/CMakeLists.txt +++ b/doc/CMakeLists.txt @@ -37,7 +37,7 @@ add_custom_target(syntax-cpp DEPENDS syntax-cpp-exe WORKING_DIRECTORY ${NEML2_BINARY_DIR}/doc COMMAND ${NEML2_BINARY_DIR}/doc/syntax-cpp-exe - COMMAND ${Python_EXECUTABLE} ${NEML2_SOURCE_DIR}/scripts/syntax_to_md.py syntax.yml content/syntax + COMMAND ${Python_EXECUTABLE} ${NEML2_SOURCE_DIR}/scripts/syntax_to_md.py syntax.yml content/syntax syntax_error.log VERBATIM ) diff --git a/scripts/syntax_to_md.py b/scripts/syntax_to_md.py index 47a10aab91..b1c6e91a2a 100755 --- a/scripts/syntax_to_md.py +++ b/scripts/syntax_to_md.py @@ -57,60 +57,77 @@ def get_sections(syntax): if __name__ == "__main__": + with open(sys.argv[1], "r") as stream: + syntax = yaml.safe_load(stream) + outdir = Path(sys.argv[2]) outdir.mkdir(parents=True, exist_ok=True) - with open(sys.argv[1], "r") as stream: - syntax = yaml.safe_load(stream) + logfile = Path(sys.argv[3]) + logfile.parent.mkdir(parents=True, exist_ok=True) - sections = get_sections(syntax) - for section in sections: - with open((outdir / section.lower()).with_suffix(".md"), "w") as stream: - stream.write( - "# [{}] {{#{}}}\n\n".format(section, "syntax-" + section.lower()) - ) - stream.write("[TOC]\n\n") - stream.write("## Available objects and their input file syntax\n\n") - stream.write( - "Refer to [System Documentation](@ref system-{}) for detailed explanation about this system.\n\n".format( - section.lower() + with open(logfile, "w") as log: + sections = get_sections(syntax) + for section in sections: + with open((outdir / section.lower()).with_suffix(".md"), "w") as stream: + stream.write( + "# [{}] {{#{}}}\n\n".format(section, "syntax-" + section.lower()) ) - ) - for type, params in syntax.items(): - if params["section"] != section: - continue - input_type = demangle(params["type"]["value"]) + stream.write("[TOC]\n\n") + stream.write("## Available objects and their input file syntax\n\n") stream.write( - "### {} {{#{}}}\n\n".format(input_type, input_type.lower()) + "Refer to [System Documentation](@ref system-{}) for detailed explanation about this system.\n\n".format( + section.lower() + ) ) - if params["doc"]: - stream.write("{}\n".format(params["doc"])) - for param_name, info in params.items(): - if param_name == "section": - continue - if param_name == "doc": - continue - if param_name == "name": + for type, params in syntax.items(): + if params["section"] != section: continue - if param_name == "type": - continue - if info["suppressed"]: - continue - - param_type = demangle(info["type"]) - param_value = postprocess(info["value"], param_type) - stream.write("

\n") - if not info["doc"]: - stream.write(" `{}`\n\n".format(param_name)) + input_type = demangle(params["type"]["value"]) + stream.write( + "### {} {{#{}}}\n\n".format(input_type, input_type.lower()) + ) + if params["doc"]: + stream.write("{}\n".format(params["doc"])) else: - stream.write( - " `{}` {}\n\n".format( - param_name, info["doc"] - ) + log.write( + "'{}' is missing object description\n".format(input_type) ) - stream.write(" - Type: {}\n".format(param_type)) - if param_value: - stream.write(" - Default: {}\n".format(param_value)) - stream.write("
\n") - stream.write("\n") - stream.write("Detailed documentation [link](@ref {})\n\n".format(type)) + for param_name, info in params.items(): + if param_name == "section": + continue + if param_name == "doc": + continue + if param_name == "name": + continue + if param_name == "type": + continue + if info["suppressed"]: + continue + + param_type = demangle(info["type"]) + param_value = postprocess(info["value"], param_type) + stream.write("
\n") + if not info["doc"]: + stream.write( + " `{}`\n\n".format(param_name) + ) + log.write( + " '{}' is mising option description\n".format( + param_name + ) + ) + else: + stream.write( + " `{}` {}\n\n".format( + param_name, info["doc"] + ) + ) + stream.write(" - Type: {}\n".format(param_type)) + if param_value: + stream.write(" - Default: {}\n".format(param_value)) + stream.write("
\n") + stream.write("\n") + stream.write( + "Detailed documentation [link](@ref {})\n\n".format(type) + ) From bcc0fa379b570caf31297d616f228dacd4b58454 Mon Sep 17 00:00:00 2001 From: Mark Messner Date: Thu, 13 Jun 2024 15:47:04 -0500 Subject: [PATCH 51/55] Action to write missing syntax to github comments --- .github/workflows/build_docs.yml | 7 +++++++ scripts/syntax_to_md.py | 1 + 2 files changed, 8 insertions(+) diff --git a/.github/workflows/build_docs.yml b/.github/workflows/build_docs.yml index 55497cfe85..f1175e899a 100644 --- a/.github/workflows/build_docs.yml +++ b/.github/workflows/build_docs.yml @@ -58,3 +58,10 @@ jobs: folder: doc/build/html clean-exclude: pr-preview/ force: false + - name: Scold users about missing docs + if: ${{ github.event_name == 'pull_request'}} + uses: thollander/actions-comment-pull-request@v2 + with: + filePath: doc/syntax_error.log + comment_tag: doc_scold + GITHUB_TOKEN: ${{ secrets.PR_ACTION }} diff --git a/scripts/syntax_to_md.py b/scripts/syntax_to_md.py index b1c6e91a2a..de293fa5a1 100755 --- a/scripts/syntax_to_md.py +++ b/scripts/syntax_to_md.py @@ -67,6 +67,7 @@ def get_sections(syntax): logfile.parent.mkdir(parents=True, exist_ok=True) with open(logfile, "w") as log: + log.write("The following syntax is missing:\n") sections = get_sections(syntax) for section in sections: with open((outdir / section.lower()).with_suffix(".md"), "w") as stream: From bda360d65f0f12d9ed3306741a984aeeaa93a6e9 Mon Sep 17 00:00:00 2001 From: Mark Messner Date: Thu, 13 Jun 2024 16:04:13 -0500 Subject: [PATCH 52/55] Some docs, a test the comment updates --- doc/content/physics/solid_mechanics.md | 2 +- scripts/syntax_to_md.py | 12 ++++++++++-- .../crystal_plasticity/ElasticStrainRate.cxx | 15 +++++++++++++++ 3 files changed, 26 insertions(+), 3 deletions(-) diff --git a/doc/content/physics/solid_mechanics.md b/doc/content/physics/solid_mechanics.md index 8cc35326b8..331eac461e 100644 --- a/doc/content/physics/solid_mechanics.md +++ b/doc/content/physics/solid_mechanics.md @@ -474,7 +474,7 @@ A full constitutive model must then define the plastic deformation $l^p$ and wha l^p = \sum_{i=1}^{n_{slip}} \dot{\gamma}_i Q \left(d_i \otimes n_i \right) Q^T \f} -where now \f$ \dot{gamma}_i \f$, the slip rate on each system, is the constitutive chioce. NEML provides a variety of options for defining these slip rates in terms of internal hardening variables and the results shear stress +where now \f$ \dot{\gamma}_i \f$, the slip rate on each system, is the constitutive chioce. NEML provides a variety of options for defining these slip rates in terms of internal hardening variables and the results shear stress \f{align*} \tau_i = \sigma : Q \operatorname{sym}\left(d_i \otimes n_i \right) Q^T diff --git a/scripts/syntax_to_md.py b/scripts/syntax_to_md.py index de293fa5a1..179ab81baf 100755 --- a/scripts/syntax_to_md.py +++ b/scripts/syntax_to_md.py @@ -67,6 +67,7 @@ def get_sections(syntax): logfile.parent.mkdir(parents=True, exist_ok=True) with open(logfile, "w") as log: + missing = 0 log.write("The following syntax is missing:\n") sections = get_sections(syntax) for section in sections: @@ -91,8 +92,11 @@ def get_sections(syntax): if params["doc"]: stream.write("{}\n".format(params["doc"])) else: + missing += 1 log.write( - "'{}' is missing object description\n".format(input_type) + " * '{}' is missing object description\n".format( + input_type + ) ) for param_name, info in params.items(): if param_name == "section": @@ -113,8 +117,9 @@ def get_sections(syntax): stream.write( " `{}`\n\n".format(param_name) ) + missing += 1 log.write( - " '{}' is mising option description\n".format( + " * '{}' is mising option description\n".format( param_name ) ) @@ -132,3 +137,6 @@ def get_sections(syntax): stream.write( "Detailed documentation [link](@ref {})\n\n".format(type) ) + + if missing == 0: + log.write("Nothing, good job! :purple_heart:") diff --git a/src/neml2/models/solid_mechanics/crystal_plasticity/ElasticStrainRate.cxx b/src/neml2/models/solid_mechanics/crystal_plasticity/ElasticStrainRate.cxx index 9824df6ac1..d9422338dc 100644 --- a/src/neml2/models/solid_mechanics/crystal_plasticity/ElasticStrainRate.cxx +++ b/src/neml2/models/solid_mechanics/crystal_plasticity/ElasticStrainRate.cxx @@ -35,14 +35,29 @@ OptionSet ElasticStrainRate::expected_options() { OptionSet options = Model::expected_options(); + + options.doc() = "Calculates the elastic strain rate as \\f$\\dot{\\varepsilon} = d - d^p - " + "\\varepsilon w + w \\varepsilon \\f$ " + "where \\f$ d \\f$ is the deformation rate, \\f$ d^p \\f$ is the plastic " + "deformation rate, \\f$ w \\f$ is the vorticity, and \\f$ \\varepsilon \\f$ is " + "the elastic strain."; + options.set("elastic_strain_rate") = VariableName("state", "elastic_strain_rate"); + options.set("elastic_strain_rate").doc() = "Name of the elastic strain rate"; + options.set("elastic_strain") = VariableName("state", "elastic_strain"); + options.set("elastic_strain").doc() = "Name of the elastic strain"; options.set("deformation_rate") = VariableName("forces", "deformation_rate"); + options.set("deformation_rate").doc() = "Name of the deformation rate"; + options.set("vorticity") = VariableName("forces", "vorticity"); + options.set("vorticity").doc() = "Name of the vorticity"; options.set("plastic_deformation_rate") = VariableName("state", "internal", "plastic_deformation_rate"); + options.set("plastic_deformation_rate").doc() = "Name of the plastic deformation rate"; + return options; } From 4614d84d042c7a2e740bca71262477e69942dafe Mon Sep 17 00:00:00 2001 From: Mark Messner Date: Thu, 13 Jun 2024 17:50:43 -0500 Subject: [PATCH 53/55] Added all my missing docs, plus a few that Gary missed --- scripts/syntax_to_md.py | 7 ++++--- .../solid_mechanics/SolidMechanicsDriver.cxx | 4 ++-- .../models/crystallography/CrystalGeometry.cxx | 12 ++++++++++++ .../models/crystallography/CubicCrystal.cxx | 3 +++ .../user_tensors/FillMillerIndex.cxx | 4 ++++ .../user_tensors/SymmetryFromOrbifold.cxx | 6 ++++++ .../crystal_plasticity/FixOrientation.cxx | 13 +++++++++++++ .../LinearSingleSlipHardeningRule.cxx | 7 +++++++ .../crystal_plasticity/OrientationRate.cxx | 17 +++++++++++++++++ .../PlasticDeformationRate.cxx | 11 +++++++++++ .../crystal_plasticity/PlasticVorticity.cxx | 16 ++++++++++++++++ .../crystal_plasticity/PowerLawSlipRule.cxx | 9 +++++++++ .../crystal_plasticity/ResolvedShear.cxx | 15 +++++++++++++++ .../SingleSlipHardeningRule.cxx | 8 ++++++++ .../SingleSlipStrengthMap.cxx | 9 +++++++++ .../crystal_plasticity/SlipRule.cxx | 9 +++++++++ .../crystal_plasticity/SlipStrengthMap.cxx | 5 +++++ .../crystal_plasticity/SumSlipRates.cxx | 7 +++++++ .../VoceSingleSlipHardeningRule.cxx | 10 ++++++++++ src/neml2/solvers/NewtonWithTrustRegion.cxx | 2 +- src/neml2/tensors/user_tensors/Orientation.cxx | 7 +++++++ 21 files changed, 175 insertions(+), 6 deletions(-) diff --git a/scripts/syntax_to_md.py b/scripts/syntax_to_md.py index 179ab81baf..c1517c3af3 100755 --- a/scripts/syntax_to_md.py +++ b/scripts/syntax_to_md.py @@ -68,7 +68,7 @@ def get_sections(syntax): with open(logfile, "w") as log: missing = 0 - log.write("The following syntax is missing:\n") + log.write("## Missing syntax\n") sections = get_sections(syntax) for section in sections: with open((outdir / section.lower()).with_suffix(".md"), "w") as stream: @@ -93,6 +93,7 @@ def get_sections(syntax): stream.write("{}\n".format(params["doc"])) else: missing += 1 + log.write( " * '{}' is missing object description\n".format( input_type @@ -119,8 +120,8 @@ def get_sections(syntax): ) missing += 1 log.write( - " * '{}' is mising option description\n".format( - param_name + " * '{}'.'{}' is mising option description\n".format( + input_type, param_name ) ) else: diff --git a/src/neml2/drivers/solid_mechanics/SolidMechanicsDriver.cxx b/src/neml2/drivers/solid_mechanics/SolidMechanicsDriver.cxx index e0b08096de..4ee9e98c16 100644 --- a/src/neml2/drivers/solid_mechanics/SolidMechanicsDriver.cxx +++ b/src/neml2/drivers/solid_mechanics/SolidMechanicsDriver.cxx @@ -43,10 +43,10 @@ SolidMechanicsDriver::expected_options() options.set("total_strain").doc() = "Total strain"; options.set("cauchy_stress") = VariableName("forces", "S"); - options.set("total_strain").doc() = "Cauchy stress"; + options.set("cauchy_stress").doc() = "Cauchy stress"; options.set("temperature") = VariableName("forces", "T"); - options.set("total_strain").doc() = "Temperature"; + options.set("temperature").doc() = "Temperature"; options.set>("prescribed_strains"); options.set("prescribed_strains").doc() = "Prescribed strain (when control = STRAIN)"; diff --git a/src/neml2/models/crystallography/CrystalGeometry.cxx b/src/neml2/models/crystallography/CrystalGeometry.cxx index 573b1a9d89..cb0e9bf13c 100644 --- a/src/neml2/models/crystallography/CrystalGeometry.cxx +++ b/src/neml2/models/crystallography/CrystalGeometry.cxx @@ -40,10 +40,22 @@ OptionSet CrystalGeometry::expected_options() { OptionSet options = Data::expected_options(); + + options.doc() = + "A Data object storing basic crystallographic information for a given crystal system."; + options.set>("crystal_class"); + options.set("crystal_class").doc() = "The set of symmetry operations defining the crystal class."; + options.set>("lattice_vectors"); + options.set("lattice_vectors").doc() = + "The three lattice vectors defining the crystal translational symmetry"; + options.set>("slip_directions"); + options.set("slip_directions").doc() = "A list of Miller indices defining the slip directions"; + options.set>("slip_planes"); + options.set("slip_planes").doc() = "A list of Miller indices defining the slip planes"; return options; } diff --git a/src/neml2/models/crystallography/CubicCrystal.cxx b/src/neml2/models/crystallography/CubicCrystal.cxx index 0a6261502a..f844f22d05 100644 --- a/src/neml2/models/crystallography/CubicCrystal.cxx +++ b/src/neml2/models/crystallography/CubicCrystal.cxx @@ -38,11 +38,14 @@ OptionSet CubicCrystal::expected_options() { OptionSet options = CrystalGeometry::expected_options(); + options.doc() = + "A specialization of the general CrystalGeometry class defining a cubic crystal system."; options.set("crystal_class").suppressed() = true; options.set("lattice_vectors").suppressed() = true; options.set>("lattice_parameter"); + options.set("lattice_parameter").doc() = "The lattice parameter \\f$ a \\f$"; return options; } diff --git a/src/neml2/models/crystallography/user_tensors/FillMillerIndex.cxx b/src/neml2/models/crystallography/user_tensors/FillMillerIndex.cxx index d6d9e78857..4fde43896a 100644 --- a/src/neml2/models/crystallography/user_tensors/FillMillerIndex.cxx +++ b/src/neml2/models/crystallography/user_tensors/FillMillerIndex.cxx @@ -35,7 +35,11 @@ OptionSet FillMillerIndex::expected_options() { OptionSet options = UserTensor::expected_options(); + options.doc() = "Fills a tensor of Miller indices from a list of integers."; + options.set>("values"); + options.set("values").doc() = "List of integers defining a Miller index, use -1 instead of \\f$ " + "\\bar{1} \\f$."; return options; } diff --git a/src/neml2/models/crystallography/user_tensors/SymmetryFromOrbifold.cxx b/src/neml2/models/crystallography/user_tensors/SymmetryFromOrbifold.cxx index 8892001767..70750d0dc8 100644 --- a/src/neml2/models/crystallography/user_tensors/SymmetryFromOrbifold.cxx +++ b/src/neml2/models/crystallography/user_tensors/SymmetryFromOrbifold.cxx @@ -36,7 +36,13 @@ OptionSet SymmetryFromOrbifold::expected_options() { OptionSet options = UserTensor::expected_options(); + options.doc() = "Returns a tensor of symmetry operations for a given symmetr group represented " + "in orbifold notation."; + options.set("orbifold"); + options.set("orbifold").doc() = + "A string giving the orbifold representation of the group, for example 432 for the typical " + "cubic crystal system defined by chiral octahedral symmetry"; return options; } diff --git a/src/neml2/models/solid_mechanics/crystal_plasticity/FixOrientation.cxx b/src/neml2/models/solid_mechanics/crystal_plasticity/FixOrientation.cxx index 5bb51f7f16..3b127760fe 100644 --- a/src/neml2/models/solid_mechanics/crystal_plasticity/FixOrientation.cxx +++ b/src/neml2/models/solid_mechanics/crystal_plasticity/FixOrientation.cxx @@ -35,9 +35,22 @@ OptionSet FixOrientation::expected_options() { OptionSet options = Model::expected_options(); + + options.doc() = + "Checks the value of the modified Rodrigues parameter by checking if " + "\\f$ \\left\\lVert r \\right\\rVert > t \\f$, with \\f$ t \\f$ a threshold value set " + "to 1.0 by default and replacing all the orientations that exceed this limit " + "with their shadow parameters values."; + options.set("input_orientation") = VariableName("state", "orientation"); + options.set("input_orientation").doc() = "Name of input tensor of orientations to operate on."; + options.set("output_orientation") = VariableName("state", "orientation"); + options.set("output_orientation").doc() = "Name of output tensor"; + options.set("threshold") = 1.0; + options.set("threshold").doc() = "Threshold value for translating to the shadow parameters"; + return options; } diff --git a/src/neml2/models/solid_mechanics/crystal_plasticity/LinearSingleSlipHardeningRule.cxx b/src/neml2/models/solid_mechanics/crystal_plasticity/LinearSingleSlipHardeningRule.cxx index aaad0a6aed..7405639c6c 100644 --- a/src/neml2/models/solid_mechanics/crystal_plasticity/LinearSingleSlipHardeningRule.cxx +++ b/src/neml2/models/solid_mechanics/crystal_plasticity/LinearSingleSlipHardeningRule.cxx @@ -32,7 +32,14 @@ OptionSet LinearSingleSlipHardeningRule::expected_options() { OptionSet options = SingleSlipHardeningRule::expected_options(); + + options.doc() = "Simple linear slip system hardening defined by \\f$ \\dot{\\tau} = \\theta " + "\\sum_{i=1}^{n_{slip}} \\left| \\dot{\\gamma}_i \\right| \\f$ where \\f$ " + "\\theta \\f$ is the hardening slope."; + options.set>("hardening_slope"); + options.set("hardening_slope").doc() = "Hardening rate"; + return options; } diff --git a/src/neml2/models/solid_mechanics/crystal_plasticity/OrientationRate.cxx b/src/neml2/models/solid_mechanics/crystal_plasticity/OrientationRate.cxx index fc12159159..f1ede23b66 100644 --- a/src/neml2/models/solid_mechanics/crystal_plasticity/OrientationRate.cxx +++ b/src/neml2/models/solid_mechanics/crystal_plasticity/OrientationRate.cxx @@ -35,13 +35,30 @@ OptionSet OrientationRate::expected_options() { OptionSet options = Model::expected_options(); + + options.doc() = + "Defines the rate of the crystal orientations as a spin given by \\f$ \\Omega^e = " + "w - w^p - \\varepsilon d^p + d^p \\varepsilon \\f$ where \\f$ \\Omega^e = \\dot{Q} Q^T " + "\\f$, \\f$ Q \\f$ is the orientation, \\f$ w \\f$ is the vorticity, \\f$ w^p \\f$ is the " + "plastic vorticity, \\f$ d^p \\f$ is the plastic deformation rate, and \\f$ \\varepsilon " + "\\f$ is the elastic stretch."; + options.set("orientation_rate") = VariableName("state", "orientation_rate"); + options.set("orientation_rate").doc() = "The name of the orientation rate (spin)"; + options.set("elastic_strain") = VariableName("state", "elastic_strain"); + options.set("elastic_strain").doc() = "The name of the elastic strain tensor"; + options.set("vorticity") = VariableName("forces", "vorticity"); + options.set("vorticity").doc() = "The name of the voriticty tensor"; + options.set("plastic_deformation_rate") = VariableName("state", "internal", "plastic_deformation_rate"); + options.set("plastic_deformation_rate").doc() = "The name of the plastic deformation rate"; + options.set("plastic_vorticity") = VariableName("state", "internal", "plastic_vorticity"); + options.set("plastic_vorticity").doc() = "The name of the plastic vorticity"; return options; } diff --git a/src/neml2/models/solid_mechanics/crystal_plasticity/PlasticDeformationRate.cxx b/src/neml2/models/solid_mechanics/crystal_plasticity/PlasticDeformationRate.cxx index f93aa2c3e9..c307a3fab0 100644 --- a/src/neml2/models/solid_mechanics/crystal_plasticity/PlasticDeformationRate.cxx +++ b/src/neml2/models/solid_mechanics/crystal_plasticity/PlasticDeformationRate.cxx @@ -37,14 +37,25 @@ PlasticDeformationRate::expected_options() { OptionSet options = Model::expected_options(); + options.doc() = "Caclulates the plastic deformation rate as \\f$ d^p = \\sum_{i=1}^{n_{slip}} " + "\\dot{\\gamma}_i Q \\operatorname{sym}{\\left(d_i \\otimes n_i \\right)} Q^T " + "\\f$ with \\f$ d^p \\f$ the plastic deformation rate, \\f$ \\dot{\\gamma}_i " + "\\f$ the slip rate on the ith slip system, \\f$Q \\f$ the orientation, \\f$ d_i " + "\\f$ the slip system direction, and \\f$ n_i \\f$ the slip system normal."; + options.set("plastic_deformation_rate") = VariableName("state", "internal", "plastic_deformation_rate"); + options.set("plastic_deformation_rate").doc() = "The name of the plastic deformation rate tensor"; options.set("orientation") = VariableName("state", "orientation_matrix"); + options.set("orientation").doc() = "The name of the orientation matrix tensor"; options.set("slip_rates") = VariableName("state", "internal", "slip_rates"); + options.set("slip_rates").doc() = "The name of the tensor containg the current slip rates"; options.set("crystal_geometry_name") = "crystal_geometry"; + options.set("crystal_geometry_name").doc() = + "The name of the Data object containing the crystallographic information for the material"; return options; } diff --git a/src/neml2/models/solid_mechanics/crystal_plasticity/PlasticVorticity.cxx b/src/neml2/models/solid_mechanics/crystal_plasticity/PlasticVorticity.cxx index 38de1a975e..e218fac53c 100644 --- a/src/neml2/models/solid_mechanics/crystal_plasticity/PlasticVorticity.cxx +++ b/src/neml2/models/solid_mechanics/crystal_plasticity/PlasticVorticity.cxx @@ -36,11 +36,27 @@ OptionSet PlasticVorticity::expected_options() { OptionSet options = Model::expected_options(); + + options.doc() = "Caclulates the plastic vorcitity as \\f$ w^p = \\sum_{i=1}^{n_{slip}} " + "\\dot{\\gamma}_i Q \\operatorname{skew}{\\left(d_i \\otimes n_i \\right)} Q^T " + "\\f$ with \\f$ d^p \\f$ the plastic deformation rate, \\f$ \\dot{\\gamma}_i " + "\\f$ the slip rate on the ith slip system, \\f$Q \\f$ the orientation, \\f$ d_i " + "\\f$ the slip system direction, and \\f$ n_i \\f$ the slip system normal."; + options.set("plastic_vorticity") = VariableName("state", "internal", "plastic_vorticity"); + options.set("plastic_vorticity").doc() = "The name of the plastic vorticity tensor"; + options.set("orientation") = VariableName("state", "orientation_matrix"); + options.set("orientation").doc() = "The name of the orientation matrix tensor"; + options.set("slip_rates") = VariableName("state", "internal", "slip_rates"); + options.set("slip_rates").doc() = "The name of the tensor containg the current slip rates"; + options.set("crystal_geometry_name") = "crystal_geometry"; + options.set("crystal_geometry_name").doc() = + "The name of the Data object containing the crystallographic information for the material"; + return options; } diff --git a/src/neml2/models/solid_mechanics/crystal_plasticity/PowerLawSlipRule.cxx b/src/neml2/models/solid_mechanics/crystal_plasticity/PowerLawSlipRule.cxx index 63801849ba..d60d9f09cf 100644 --- a/src/neml2/models/solid_mechanics/crystal_plasticity/PowerLawSlipRule.cxx +++ b/src/neml2/models/solid_mechanics/crystal_plasticity/PowerLawSlipRule.cxx @@ -33,9 +33,18 @@ OptionSet PowerLawSlipRule::expected_options() { OptionSet options = SlipRule::expected_options(); + options.doc() = + "Power law slip rule defined as \\f$ \\dot{\\gamma}_i = \\dot{\\gamma}_0 \\left| " + "\\frac{\\tau_i}{\\hat{\\tau}_i} \\right|^{n-1} \\frac{\\tau_i}{\\hat{\\tau}_i} \\f$ with " + "\\f$ \\dot{\\gamma}_i \\f$ the slip rate on system \\f$ i \\f$, \\f$ \\tau_i \\f$ the " + "resolved shear, \\f$ \\hat{\\tau}_i \\f$ the slip system strength, \\f$ n \\f$ the rate " + "senstivity, and \\f$ \\dot{\\gamma}_0 \\f$ a reference slip rate."; options.set>("gamma0"); + options.set("gamma0").doc() = "Reference slip rate"; + options.set>("n"); + options.set("n").doc() = "Rate sensitivity exponent"; return options; } diff --git a/src/neml2/models/solid_mechanics/crystal_plasticity/ResolvedShear.cxx b/src/neml2/models/solid_mechanics/crystal_plasticity/ResolvedShear.cxx index 7481ddeac8..d60af69d93 100644 --- a/src/neml2/models/solid_mechanics/crystal_plasticity/ResolvedShear.cxx +++ b/src/neml2/models/solid_mechanics/crystal_plasticity/ResolvedShear.cxx @@ -36,11 +36,26 @@ OptionSet ResolvedShear::expected_options() { OptionSet options = Model::expected_options(); + + options.doc() = "Calculates the resolved shears as \\f$ \\tau_i = \\sigma : Q " + "\\operatorname{sym}\\left(d_i \\otimes n_i \\right) Q^T \\f$ where \\f$ \\tau_i " + "\\f$ is the resolved shear on slip system i, \\f$ \\sigma \\f$ is the Cauchy " + "stress \\f$ Q \\f$ is the orientation matrix, \\f$ d_i \\f$ is the slip " + "direction, and \\f$ n_i \\f$ is the slip system normal."; + options.set("resolved_shears") = VariableName("state", "internal", "resolved_shears"); + options.set("resolved_shears").doc() = "The name of the resolved shears"; + options.set("stress") = VariableName("state", "internal", "cauchy_stress"); + options.set("stress").doc() = "The name of the Cauchy stress tensor"; + options.set("orientation") = VariableName("state", "orientation_matrix"); + options.set("orientation").doc() = "The name of the orientation matrix"; + options.set("crystal_geometry_name") = "crystal_geometry"; + options.set("crystal_geometry_name").doc() = + "The name of the data object with the crystallographic information"; return options; } diff --git a/src/neml2/models/solid_mechanics/crystal_plasticity/SingleSlipHardeningRule.cxx b/src/neml2/models/solid_mechanics/crystal_plasticity/SingleSlipHardeningRule.cxx index 9666b0d919..75e49fca35 100644 --- a/src/neml2/models/solid_mechanics/crystal_plasticity/SingleSlipHardeningRule.cxx +++ b/src/neml2/models/solid_mechanics/crystal_plasticity/SingleSlipHardeningRule.cxx @@ -33,10 +33,18 @@ OptionSet SingleSlipHardeningRule::expected_options() { OptionSet options = Model::expected_options(); + + options.doc() = + "Parent class of slip hardening rules where all slip systems share the same strength."; + options.set("slip_hardening_rate") = VariableName("state", "internal", "slip_hardening_rate"); + options.set("slip_hardening_rate").doc() = + "Name of tensor to output the slip system hardening rates into"; options.set("slip_hardening") = VariableName("state", "internal", "slip_hardening"); + options.set("slip_hardening").doc() = "Name of current values of slip hardening"; options.set("sum_slip_rates") = VariableName("state", "internal", "sum_slip_rates"); + options.set("sum_slip_rates").doc() = "Name of tensor containing the sum of the slip rates"; return options; } diff --git a/src/neml2/models/solid_mechanics/crystal_plasticity/SingleSlipStrengthMap.cxx b/src/neml2/models/solid_mechanics/crystal_plasticity/SingleSlipStrengthMap.cxx index 8d8335d441..71fd2e25a7 100644 --- a/src/neml2/models/solid_mechanics/crystal_plasticity/SingleSlipStrengthMap.cxx +++ b/src/neml2/models/solid_mechanics/crystal_plasticity/SingleSlipStrengthMap.cxx @@ -33,8 +33,17 @@ SingleSlipStrengthMap::expected_options() { OptionSet options = SlipStrengthMap::expected_options(); + options.doc() = + "Calculates the slip system strength for all slip systems as \\f$ \\hat{\\tau}_i = " + "\\bar{\\tau} + \\tau_0 \\f$ where \\f$ \\hat{\\tau}_i \\f$ is the strength for slip system " + "i, \\f$ \\bar{\\tau} \\f$ is an evolving slip system strength (one value of all systems), " + "defined by another object, and \\f$ \\tau_0 \\f$ is a constant strength."; + options.set("slip_hardening") = VariableName("state", "internal", "slip_hardening"); + options.set("slip_hardening").doc() = "The name of the evovling, scalar strength"; + options.set>("constant_strength"); + options.set("constant_strength").doc() = "The constant slip system strength"; return options; } diff --git a/src/neml2/models/solid_mechanics/crystal_plasticity/SlipRule.cxx b/src/neml2/models/solid_mechanics/crystal_plasticity/SlipRule.cxx index cc147e2787..965f26e8d2 100644 --- a/src/neml2/models/solid_mechanics/crystal_plasticity/SlipRule.cxx +++ b/src/neml2/models/solid_mechanics/crystal_plasticity/SlipRule.cxx @@ -35,13 +35,22 @@ SlipRule::expected_options() { OptionSet options = Model::expected_options(); + options.doc() = "Parent class for all slip rules, which define the slip rate in terms of the " + "resolved shear and the slip system strength"; + options.set("slip_rates") = VariableName("state", "internal", "slip_rates"); + options.set("slip_rates").doc() = "Name of the slip rate tensor"; options.set("resolved_shears") = VariableName("state", "internal", "resolved_shears"); + options.set("resolved_shears").doc() = "Name of the resolved shear tensor"; + options.set("slip_strengths") = VariableName("state", "internal", "slip_strengths"); + options.set("slip_strengths").doc() = "Name of the tensor containing the slip system strengths"; options.set("crystal_geometry_name") = "crystal_geometry"; + options.set("crystal_geometry_name").doc() = + "Name of the Data object containing the crystallographic information"; return options; } diff --git a/src/neml2/models/solid_mechanics/crystal_plasticity/SlipStrengthMap.cxx b/src/neml2/models/solid_mechanics/crystal_plasticity/SlipStrengthMap.cxx index 2d8db6098e..634fe6844d 100644 --- a/src/neml2/models/solid_mechanics/crystal_plasticity/SlipStrengthMap.cxx +++ b/src/neml2/models/solid_mechanics/crystal_plasticity/SlipStrengthMap.cxx @@ -34,9 +34,14 @@ OptionSet SlipStrengthMap::expected_options() { OptionSet options = Model::expected_options(); + options.doc() = "Map between internal variables the slip system strengths."; options.set("slip_strengths") = VariableName("state", "internal", "slip_strengths"); + options.set("slip_strengths").doc() = "Name of the slip system strengths"; + options.set("crystal_geometry_name") = "crystal_geometry"; + options.set("crystal_geometry_name").doc() = + "Name of the Data object containing the crystallographic information"; return options; } diff --git a/src/neml2/models/solid_mechanics/crystal_plasticity/SumSlipRates.cxx b/src/neml2/models/solid_mechanics/crystal_plasticity/SumSlipRates.cxx index e488d879f0..25efc0c440 100644 --- a/src/neml2/models/solid_mechanics/crystal_plasticity/SumSlipRates.cxx +++ b/src/neml2/models/solid_mechanics/crystal_plasticity/SumSlipRates.cxx @@ -37,11 +37,18 @@ OptionSet SumSlipRates::expected_options() { OptionSet options = Model::expected_options(); + options.doc() = "Calculates the sum of the absolute value of all the slip rates as \\f$ " + "\\sum_{i=1}^{n_{slip}} \\left| \\dot{\\gamma}_i \\right| \\f$."; options.set("slip_rates") = VariableName("state", "internal", "slip_rates"); + options.set("slip_rates").doc() = "The name of individual slip rates"; + options.set("sum_slip_rates") = VariableName("state", "internal", "sum_slip_rates"); + options.set("sum_slip_rates").doc() = "The outut name for the scalar sum of the slip rates"; options.set("crystal_geometry_name") = "crystal_geometry"; + options.set("crystal_geometry_name").doc() = + "The name of the Data object containing the crystallographic information"; return options; } diff --git a/src/neml2/models/solid_mechanics/crystal_plasticity/VoceSingleSlipHardeningRule.cxx b/src/neml2/models/solid_mechanics/crystal_plasticity/VoceSingleSlipHardeningRule.cxx index d0339df153..efa76e4d26 100644 --- a/src/neml2/models/solid_mechanics/crystal_plasticity/VoceSingleSlipHardeningRule.cxx +++ b/src/neml2/models/solid_mechanics/crystal_plasticity/VoceSingleSlipHardeningRule.cxx @@ -32,8 +32,18 @@ OptionSet VoceSingleSlipHardeningRule::expected_options() { OptionSet options = SingleSlipHardeningRule::expected_options(); + options.doc() = "Voce hardening for a SingleSlipStrength type model defined by \\f$ \\dot{\\tau} " + "= \\theta_0 \\left( 1 - \\frac{\\tau}{\\tau_f} \\right) " + "\\sum_{i=1}^{n_{slip}} \\left| \\dot{\\gamma}_i \\right| \\f$ where \\f$ " + "\\theta_0 \\f$ is the initial rate of work hardening, \\f$ \\tau_f \\f$ is the " + "saturated, maximum value of the slip system strength, and \\f$ \\dot{\\gamma}_i " + "\\f$ is the slip rate on each system."; + options.set>("initial_slope"); + options.set("initial_slope").doc() = "The initial rate of hardening"; options.set>("saturated_hardening"); + options.set("saturated_hardening").doc() = + "The final, saturated value of the slip system strength"; return options; } diff --git a/src/neml2/solvers/NewtonWithTrustRegion.cxx b/src/neml2/solvers/NewtonWithTrustRegion.cxx index 919777074d..74773ab24e 100644 --- a/src/neml2/solvers/NewtonWithTrustRegion.cxx +++ b/src/neml2/solvers/NewtonWithTrustRegion.cxx @@ -68,7 +68,7 @@ NewtonWithTrustRegion::expected_options() options.set("subproblem_rel_tol").doc() = "Relative tolerance used for the quadratic sub-problem"; options.set("subproblem_abs_tol") = 1e-8; - options.set("subproblem_rel_tol").doc() = "Absolute tolerance used for the quadratic sub-problem"; + options.set("subproblem_abs_tol").doc() = "Absolute tolerance used for the quadratic sub-problem"; options.set("subproblem_max_its") = 10; options.set("subproblem_max_its").doc() = diff --git a/src/neml2/tensors/user_tensors/Orientation.cxx b/src/neml2/tensors/user_tensors/Orientation.cxx index cbe423fcc2..bdeb53fff9 100644 --- a/src/neml2/tensors/user_tensors/Orientation.cxx +++ b/src/neml2/tensors/user_tensors/Orientation.cxx @@ -36,6 +36,13 @@ OptionSet Orientation::expected_options() { OptionSet options = UserTensor::expected_options(); + + options.doc() = "An orientation, internally defined as a set of Modified Rodrigues parameters " + "given by \\f$ r = n \\tan{\\frac{\\theta}{4}} \\f$ with \\f$ n \\f$ the axis of " + "rotation and \\f$ \\theta \\f$ the rotation angle about that axis. However, " + "this class provides a variety of ways to define the orientation in terms of " + "other, more common representations."; + options.set("input_type") = "euler_angles"; options.set("input_type").doc() = "The method used to define the angles, 'euler_angles' or 'random'"; From dac6b3393b23591eb9e37351411ac272a2b98b84 Mon Sep 17 00:00:00 2001 From: Mark Messner Date: Thu, 13 Jun 2024 18:02:36 -0500 Subject: [PATCH 54/55] Hmm, learn to spell Gary --- scripts/syntax_to_md.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/scripts/syntax_to_md.py b/scripts/syntax_to_md.py index c1517c3af3..bd475b1f99 100755 --- a/scripts/syntax_to_md.py +++ b/scripts/syntax_to_md.py @@ -120,7 +120,7 @@ def get_sections(syntax): ) missing += 1 log.write( - " * '{}'.'{}' is mising option description\n".format( + " * '{}'.'{}' is missing option description\n".format( input_type, param_name ) ) From 27dd9881bd6cabd69b97f9791096f03deafa5caf Mon Sep 17 00:00:00 2001 From: Gary Hu Date: Thu, 13 Jun 2024 19:04:35 -0500 Subject: [PATCH 55/55] Relocate latex formulae in option docstring --- src/neml2/models/crystallography/CubicCrystal.cxx | 4 ++-- .../models/crystallography/user_tensors/FillMillerIndex.cxx | 6 +++--- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/src/neml2/models/crystallography/CubicCrystal.cxx b/src/neml2/models/crystallography/CubicCrystal.cxx index f844f22d05..16d55da49c 100644 --- a/src/neml2/models/crystallography/CubicCrystal.cxx +++ b/src/neml2/models/crystallography/CubicCrystal.cxx @@ -45,7 +45,7 @@ CubicCrystal::expected_options() options.set("lattice_vectors").suppressed() = true; options.set>("lattice_parameter"); - options.set("lattice_parameter").doc() = "The lattice parameter \\f$ a \\f$"; + options.set("lattice_parameter").doc() = "The lattice parameter"; return options; } @@ -59,4 +59,4 @@ CubicCrystal::CubicCrystal(const OptionSet & options) } } // namespace crystallography -} // namespace neml2 \ No newline at end of file +} // namespace neml2 diff --git a/src/neml2/models/crystallography/user_tensors/FillMillerIndex.cxx b/src/neml2/models/crystallography/user_tensors/FillMillerIndex.cxx index 4fde43896a..051736bed1 100644 --- a/src/neml2/models/crystallography/user_tensors/FillMillerIndex.cxx +++ b/src/neml2/models/crystallography/user_tensors/FillMillerIndex.cxx @@ -35,11 +35,11 @@ OptionSet FillMillerIndex::expected_options() { OptionSet options = UserTensor::expected_options(); - options.doc() = "Fills a tensor of Miller indices from a list of integers."; + options.doc() = "Fills a tensor of Miller indices from a list of integers. Use -1 instead of " + "\\f$ \\bar{1} \\f$."; options.set>("values"); - options.set("values").doc() = "List of integers defining a Miller index, use -1 instead of \\f$ " - "\\bar{1} \\f$."; + options.set("values").doc() = "List of integers defining a Miller index"; return options; }